diff --git a/README.md b/README.md index c2da62c6..87d60d34 100644 --- a/README.md +++ b/README.md @@ -655,6 +655,8 @@ Parameters for the `custom:frigate-card-menu-submenu` element are identical to t | `type` | Must be `custom:frigate-card-menu-submenu`. | | `items` | A list of menu items, as described below. | + + ##### Submenu Items | Parameter | Default | Description | @@ -669,6 +671,19 @@ Parameters for the `custom:frigate-card-menu-submenu` element are identical to t See the [Configuring a Submenu example](#configuring-a-submenu-example). +#### `custom:frigate-card-menu-submenu-select` + +This element allows you to easily convert a [Home Assistant Select Entity](https://www.home-assistant.io/integrations/select/) or [Home Assistant Input Select Entity](https://www.home-assistant.io/integrations/input_select/) (an entity either starting with `select` or `input_select`) into an overridable submenu. This *could* be done by hand using a regular submenu (above) -- this element is a convenience. + +Parameters for the `custom:frigate-card-menu-submenu-select` element are identical to the parameters of the [stock Home Assistant State Icon Element](https://www.home-assistant.io/dashboards/picture-elements/#state-icon) with the exception of these parameters which differ: + +| Parameter | Description | +| - | - | +| `type` | Must be `custom:frigate-card-menu-submenu-select`. | +| `options` | An optional dictionary of overrides keyed by the option name that the given select entity supports. These options can be used to set or override submenu item parameters on a per-option basis. The format is as described in [Submenu Items](#frigate-card-submenu-items) above.| + +See the [Configuring a Select Submenu example](#configuring-a-select-submenu-example). + #### `custom:frigate-card-conditional` @@ -1210,6 +1225,55 @@ elements: + + +### Configuring a `select` submenu + +You can easily add a submenu to the menu based on a `select` or `input_select` entity. + +
+ Expand: Adding a select submenu + +This example imagines the user has an `input_select` entity configured in their Home Assistant configuration like so: + +```yaml +input_select: + kitchen_scene: + name: Kitchen Scene Select + options: + - scene.kitchen_cooking_scene + - scene.kitchen_tv_scene + icon: mdi:lightbulb +``` + +The following will convert this entity into a submenu: + +```yaml +[...] +elements: + - type: custom:frigate-card-menu-submenu-select + icon: mdi:lamps + entity: input_select.kitchen_scene +``` + +To override 1 or more individual options (e.g. to set custom icons and titles) + +```yaml +[...] +elements: + - type: custom:frigate-card-menu-submenu-select + icon: mdi:lamps + entity: input_select.kitchen_scene + options: + scene.kitchen_cooking_scene: + icon: mdi:chef-hat + title: Cooking time! + scene.kitchen_dining_scene: + icon: mdi:television + title: TV! +``` + +
### Overriding card behavior diff --git a/rollup.config.js b/rollup.config.js index d6d1f407..daf92929 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -9,7 +9,8 @@ import styles from 'rollup-plugin-styles'; import image from '@rollup/plugin-image'; import replace from '@rollup/plugin-replace'; -const dev = process.env.ROLLUP_WATCH === 'true' || process.env.ROLLUP_WATCH === '1'; +const watch = process.env.ROLLUP_WATCH === 'true' || process.env.ROLLUP_WATCH === '1'; +const dev = watch || process.env.DEV === 'true' || process.env.DEV === '1'; const serveopts = { contentBase: ['./dist'], @@ -50,7 +51,7 @@ const plugins = [ 'process.env.NODE_ENV': JSON.stringify(dev ? 'development' : 'production'), } }), - dev && serve(serveopts), + watch && serve(serveopts), !dev && terser(), ]; diff --git a/src/components/elements.ts b/src/components/elements.ts index 11e6648e..71578269 100644 --- a/src/components/elements.ts +++ b/src/components/elements.ts @@ -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 {} + +@customElement('frigate-card-menu-submenu-select') +export class FrigateCardElementsMenuSubmenuSelect extends FrigateCardElementsBaseMenuIcon {} diff --git a/src/components/menu.ts b/src/components/menu.ts index 3336289b..07888542 100644 --- a/src/components/menu.ts +++ b/src/components/menu.ts @@ -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` `; + } else if (button.type === 'custom:frigate-card-menu-submenu-select') { + return html` + `; } let stateParameters: StateParameters = { ...button }; diff --git a/src/components/submenu.ts b/src/components/submenu.ts index 2e25a398..41b6e3a4 100644 --- a/src/components/submenu.ts +++ b/src/components/submenu.ts @@ -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` ${stateParameters.title || ''} - ${item.subtitle - ? html`${item.subtitle}` - : ''} + ${item.subtitle ? html`${item.subtitle}` : ''} ${stateParameters.icon ? html` `; + } +} diff --git a/src/types.ts b/src/types.ts index f66ea1f6..b9c2fa27 100644 --- a/src/types.ts +++ b/src/types.ts @@ -436,12 +436,21 @@ const menuSubmenuItemSchema = elementsBaseSchema.extend({ }); export type MenuSubmenuItem = z.infer; -export const menuSubmenuSchema = menuBaseSchema.merge(iconSchema).extend({ +const menuSubmenuSchema = menuBaseSchema.merge(iconSchema).extend({ type: z.literal('custom:frigate-card-menu-submenu'), items: menuSubmenuItemSchema.array(), }); export type MenuSubmenu = z.infer; -export type MenuItem = MenuIcon | MenuStateIcon | MenuSubmenu; + +const menuSubmenuSelectSchema = menuBaseSchema.merge(stateIconSchema).extend({ + type: z.literal('custom:frigate-card-menu-submenu-select'), + // Please ensure additions to this type are filtered out appropriately in + // `submenu.ts` when this is converted to a MenuSubmenu. + options: z.record(menuSubmenuItemSchema).optional(), +}); +export type MenuSubmenuSelect = z.infer; + +export type MenuItem = MenuIcon | MenuStateIcon | MenuSubmenu | MenuSubmenuSelect; const frigateCardConditionSchema = z.object({ view: z.string().array().optional(), @@ -464,6 +473,7 @@ const pictureElementSchema = z.union([ menuStateIconSchema, menuIconSchema, menuSubmenuSchema, + menuSubmenuSelectSchema, frigateConditionalSchema, stateBadgeIconSchema, stateIconSchema, @@ -1072,6 +1082,7 @@ const menuButtonSchema = z.discriminatedUnion('type', [ menuIconSchema, menuStateIconSchema, menuSubmenuSchema, + menuSubmenuSelectSchema, ]); export type MenuButton = z.infer; export interface ExtendedHomeAssistant extends HomeAssistant {