diff --git a/docs/_coverpage.md b/docs/_coverpage.md index d0ee729e..c1cb5d64 100644 --- a/docs/_coverpage.md +++ b/docs/_coverpage.md @@ -1,4 +1,4 @@ -![logo](images/frigate-logo.svg) +![logo](images/icons/frigate.svg) # Frigate Card 6.0.0 diff --git a/docs/configuration/elements/custom/README.md b/docs/configuration/elements/custom/README.md index 3b847998..208a526e 100644 --- a/docs/configuration/elements/custom/README.md +++ b/docs/configuration/elements/custom/README.md @@ -37,7 +37,7 @@ Configuration is similar to a stock [Picture Elements Icon](https://www.home-ass | `type` | | Must be `custom:frigate-card-menu-icon`. | | `enabled` | `true` | Whether or not to show this menu item. | | `entity` | | An optional Home Assistant entity for use with actions. | -| `icon` | | An required icon to display, e.g. `mdi:car` | +| `icon` | | An required icon to display, e.g. `mdi:car`. See also [custom icons](../../../usage/custom-icons.md). | | `permanent` | `false` | If `false` the menu item is hidden when the menu has the `hidden` style and the menu is closed, otherwise it is shown (and sorted to the front). | | `priority` | `50` | The menu item priority. Higher priority item are ordered closer to the start of the menu alignment (i.e. a button with priority `70` will order further to the left than a button with priority `60`, when the menu alignment is `left`). Minimum `0`, maximum `100`. | | `style` | | Position and style the element using CSS. | @@ -59,7 +59,7 @@ elements: | `type` | | Must be `custom:frigate-card-menu-submenu`. | | `enabled` | `true` | Whether or not to show this menu item. | | `entity` | | An optional Home Assistant entity for use with actions. | -| `icon` | | An required icon to display, e.g. `mdi:car` | +| `icon` | | An required icon to display, e.g. `mdi:car`. See also [custom icons](../../../usage/custom-icons.md). | | `items` | | A list of menu items, as described below. | | `permanent` | `false` | If `false` the menu item is hidden when the menu has the `hidden` style and the menu is closed, otherwise it is shown (and sorted to the front). | | `priority` | `50` | The menu item priority. Higher priority item are ordered closer to the start of the menu alignment (i.e. a button with priority `70` will order further to the left than a button with priority `60`, when the menu alignment is `left`). Minimum `0`, maximum `100`. | @@ -69,16 +69,16 @@ elements: ### Submenu items -| Parameter | Default | Description | -| ------------------------------------------------------------------------ | ------- | ------------------------------------------------------------------------------------------------- | -| `enabled` | `true` | Whether or not to show this item as enabled / selectable. | -| `entity` | | An optional Home Assistant entity from which title, icon and style can be automatically computed. | -| `icon` | | An optional item icon to display, e.g. `mdi:car` | -| `selected` | `false` | Whether or not to show this item as selected. | -| `state_color` | `true` | Whether or not the title and icon should be stylized based on state. | -| `style` | | Position and style the element using CSS. | -| `tap_action`, `double_tap_action`, `hold_action`, `start_tap`, `end_tap` | | The [actions](../../actions/README.md) to take when this item is interacted with. | -| `title` | | An optional title to display. | +| Parameter | Default | Description | +| ------------------------------------------------------------------------ | ------- | ---------------------------------------------------------------------------------------------------------- | +| `enabled` | `true` | Whether or not to show this item as enabled / selectable. | +| `entity` | | An optional Home Assistant entity from which title, icon and style can be automatically computed. | +| `icon` | | An optional item icon to display, e.g. `mdi:car`. See also [custom icons](../../../usage/custom-icons.md). | +| `selected` | `false` | Whether or not to show this item as selected. | +| `state_color` | `true` | Whether or not the title and icon should be stylized based on state. | +| `style` | | Position and style the element using CSS. | +| `tap_action`, `double_tap_action`, `hold_action`, `start_tap`, `end_tap` | | The [actions](../../actions/README.md) to take when this item is interacted with. | +| `title` | | An optional title to display. | ## `menu-submenu-select` @@ -95,7 +95,7 @@ elements: | `type` | | Must be `custom:frigate-card-menu-submenu-select`. | | `enabled` | `true` | Whether or not to show this menu item. | | `entity` | | An optional Home Assistant entity for use with actions. | -| `icon` | | An required icon to display, e.g. `mdi:car` | +| `icon` | | An required icon to display, e.g. `mdi:car`. See also [custom icons](../../../usage/custom-icons.md). | | `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](./README.md?id=submenu-items) above. | | `permanent` | `false` | If `false` the menu item is hidden when the menu has the `hidden` style and the menu is closed, otherwise it is shown (and sorted to the front). | | `priority` | `50` | The menu item priority. Higher priority item are ordered closer to the start of the menu alignment (i.e. a button with priority `70` will order further to the left than a button with priority `60`, when the menu alignment is `left`). Minimum `0`, maximum `100`. | @@ -105,7 +105,7 @@ elements: See the `select` [submenu example](../../../examples.md?id=select-submenu). -## `state-icon` +## `menu-state-icon` Add a state icon to the Frigate Card menu that represents the state of a Home Assistant entity. @@ -122,7 +122,7 @@ Configuration is similar to a stock [Picture Elements State Icon](https://www.ho | `type` | | Must be `custom:frigate-card-menu-state-icon`. | | `enabled` | `true` | Whether or not to show this menu item. | | `entity` | | An optional Home Assistant entity for use with actions. | -| `icon` | | An required icon to display, e.g. `mdi:car` | +| `icon` | | An required icon to display, e.g. `mdi:car`. See also [custom icons](../../../usage/custom-icons.md). | | `permanent` | `false` | If `false` the menu item is hidden when the menu has the `hidden` style and the menu is closed, otherwise it is shown (and sorted to the front). | | `priority` | `50` | The menu item priority. Higher priority item are ordered closer to the start of the menu alignment (i.e. a button with priority `70` will order further to the left than a button with priority `60`, when the menu alignment is `left`). Minimum `0`, maximum `100`. | | `tap_action`, `double_tap_action`, `hold_action`, `start_tap`, `end_tap` | | The [actions](../../actions/README.md) to take when this item is interacted with. | @@ -147,7 +147,7 @@ elements: | `enabled` | `true` | `true` to enable this status bar item, `false` to disable. | | `exclusive` | `false` | Whether or not this item should evict non-exclusive items from the status bar. | | `expand` | `false` | If `false` this status bar item will consume the minimum possible space, if `true` will expand to the available space. | -| `icon` | | The icon to show in the status bar, e.g. `mdi:camera-front`. | +| `icon` | | The icon to show in the status bar, e.g. `mdi:camera-front`. See also [custom icons](../../../usage/custom-icons.md). | | `priority` | `50` | The item priority. Higher priority items are ordered closer to the start of the status bar (i.e. an item with priority `70` will order further to the left than an item with priority `60`). Minimum `0`, maximum `100`. | | `sufficient` | `false` | Whether or not this item is sufficient to display the status bar if it's otherwise hidden (e.g. with the `popup` [status bar style](../../status-bar.md)). | diff --git a/docs/configuration/menu.md b/docs/configuration/menu.md index d0ab239b..c95a8111 100644 --- a/docs/configuration/menu.md +++ b/docs/configuration/menu.md @@ -55,7 +55,7 @@ menu: | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `alignment` | `matching` | Whether this button should have an alignment that is `matching` the menu alignment or `opposing` the menu. Can be used to create two separate groups of buttons on the menu. `priority` orders buttons within a given `alignment`. | | `enabled` | `true` for `frigate`, `cameras`, `substreams`, `live`, `clips`, `snapshots`, `timeline`, `download`, `camera_ui`, `fullscreen`, `media_player`, `display_mode` and `ptz_home`. `false` for `image`, `expand`, `microphone`, `mute`, `play`, `recordings`, `screenshot`, `ptz_controls` | Whether or not to show the button. | -| `icon` | | An icon to overriding the default for that button, e.g. `mdi:camera-front`. | +| `icon` | | An icon to overriding the default for that button, e.g. `mdi:camera-front`. See also [custom icons](../usage/custom-icons.md). | | `permanent` | `false` | If `false` the menu item is hidden when the menu has the `hidden` style and the menu is closed, otherwise it is shown (and sorted to the front). | ## `style` diff --git a/docs/images/frigate-logo.svg b/docs/images/icons/frigate.svg similarity index 83% rename from docs/images/frigate-logo.svg rename to docs/images/icons/frigate.svg index 494fc4a8..bdbdc102 100644 --- a/docs/images/frigate-logo.svg +++ b/docs/images/icons/frigate.svg @@ -6,8 +6,8 @@ fill="none" version="1.1" id="svg54" - sodipodi:docname="logo.svg" - inkscape:version="1.1.1 (1:1.1+202109281949+c3084ef5ed)" + sodipodi:docname="frigate.svg" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" @@ -16,28 +16,30 @@ id="defs58" /> + showguides="true" + inkscape:showpageshadow="2" + inkscape:deskcolor="#d1d1d1" /> diff --git a/src/camera-manager/motioneye/assets/motioneye-logo.svg b/docs/images/icons/motioneye.svg similarity index 62% rename from src/camera-manager/motioneye/assets/motioneye-logo.svg rename to docs/images/icons/motioneye.svg index 28ba99bb..57c10b9c 100644 --- a/src/camera-manager/motioneye/assets/motioneye-logo.svg +++ b/docs/images/icons/motioneye.svg @@ -2,36 +2,44 @@ image/svg+xml \ No newline at end of file + style="fill:url(#linearGradient4313);fill-opacity:1;stroke:none;filter:url(#filter4327)" + inkscape:connector-curvature="0" + transform="matrix(0.38078533,0,0,0.38078533,2.9655495,31.335499)" /> diff --git a/docs/index.html b/docs/index.html index 5d79de65..973ccb40 100644 --- a/docs/index.html +++ b/docs/index.html @@ -28,6 +28,12 @@ :root { --content-max-width: 95vw; } + .svg-icon { + background-color: rgb(255, 255, 255); + vertical-align: middle; + background: repeating-conic-gradient(#8080804f 0% 25%, transparent 0% 50%) 50% / + 4px 4px; + } @@ -42,7 +48,7 @@ // The overall documentation name. name: 'Frigate Card', - logo: 'images/frigate-logo.svg', + logo: 'images/icons/frigate.svg', // Automatically go to the top of a page on route change. auto2top: true, diff --git a/docs/usage/README.md b/docs/usage/README.md index e0b8ac79..de0b2f95 100644 --- a/docs/usage/README.md +++ b/docs/usage/README.md @@ -11,6 +11,10 @@ See [2-way audio](2-way-audio.md) for documentation on using 2-way audio. See [Casting](casting.md) for documentation on casting the card. +### Custom Icons + +See [Custom Icons](custom-icons.md) for custom icons this card supports. + ### URL Actions See [URL Actions](url-actions.md) for documentation on acting based on URL contents. diff --git a/docs/usage/_sidebar.md b/docs/usage/_sidebar.md index 4d3913db..36aa93c6 100644 --- a/docs/usage/_sidebar.md +++ b/docs/usage/_sidebar.md @@ -6,6 +6,7 @@ - [Usage](README.md) - [2-way audio](2-way-audio.md) - [Casting](casting.md) + - [Custom Icons](custom-icons.md) - [Keyboard Shortcuts](keyboard-shortcuts.md) - [URL Actions](url-actions.md) diff --git a/docs/usage/custom-icons.md b/docs/usage/custom-icons.md new file mode 100644 index 00000000..fbec7f71 --- /dev/null +++ b/docs/usage/custom-icons.md @@ -0,0 +1,11 @@ +# Custom Icons + +The [`menu`](../configuration/menu.md) and +[`status_bar`](../configuration/status-bar.md) both support a small set of +builtin custom items not found in the traditional [Material Design Icons +(`mdi`)](https://pictogrammers.com/library/mdi/) set by setting the relevant `icon` paramter to the following values. + +| Icon Name | Resulting Icon | +| ----------- | --------------------------------------------------------------------------- | +| `frigate` | ![Frigate Icon](../images/icons/frigate.svg ':size=24 :class=svg-icon') | +| `motioneye` | ![motionEye Icon](../images/icons/motioneye.svg ':size=24 :class=svg-icon') | diff --git a/scripts/docs-update-icons.sh b/scripts/docs-update-icons.sh new file mode 100755 index 00000000..68c8ec95 --- /dev/null +++ b/scripts/docs-update-icons.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# This script copies icons out of the source tree for documentation. + +FRIGATE="./src/camera-manager/frigate/assets/frigate.svg" +MOTIONEYE="./src/camera-manager/motioneye/assets/motioneye.svg" +DIR_ICONS="./docs/images/icons" + +copy() { + PATH="$1" + echo "Copying image $PATH to $DIR_ICONS" + /bin/cp "$PATH" "$DIR_ICONS" +} + +copy "$FRIGATE" +copy "$MOTIONEYE" diff --git a/src/camera-manager/frigate/assets/frigate-logo-dark.svg b/src/camera-manager/frigate/assets/frigate-logo-dark.svg deleted file mode 100644 index 16cd275c..00000000 --- a/src/camera-manager/frigate/assets/frigate-logo-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/camera-manager/frigate/assets/frigate.svg b/src/camera-manager/frigate/assets/frigate.svg new file mode 100644 index 00000000..bdbdc102 --- /dev/null +++ b/src/camera-manager/frigate/assets/frigate.svg @@ -0,0 +1,45 @@ + + + + + + diff --git a/src/camera-manager/frigate/engine-frigate.ts b/src/camera-manager/frigate/engine-frigate.ts index 802874ef..615c311c 100644 --- a/src/camera-manager/frigate/engine-frigate.ts +++ b/src/camera-manager/frigate/engine-frigate.ts @@ -58,7 +58,7 @@ import { RecordingSegmentsQueryResultsMap, } from '../types'; import { getDefaultGo2RTCEndpoint } from '../utils/go2rtc-endpoint'; -import frigateLogo from './assets/frigate-logo-dark.svg'; +import frigateLogo from './assets/frigate.svg'; import { FrigateCamera, isBirdseye } from './camera'; import { FrigateEventWatcher } from './event-watcher'; import { FrigateViewMediaFactory } from './media'; diff --git a/src/camera-manager/frigate/icon.ts b/src/camera-manager/frigate/icon.ts deleted file mode 100644 index 3e075f2e..00000000 --- a/src/camera-manager/frigate/icon.ts +++ /dev/null @@ -1,20 +0,0 @@ -export const FRIGATE_ICON_SVG_PATH = - 'm 4.8759466,22.743573 c 0.0866,0.69274 0.811811,1.16359 0.37885,1.27183 ' + - '-0.43297,0.10824 -2.32718,-3.43665 -2.7601492,-4.95202 -0.4329602,-1.51538 ' + - '-0.6764993,-3.22017 -0.5682593,-4.19434 0.1082301,-0.97417 5.7097085,-2.48955 ' + - '5.7097085,-2.89545 0,-0.4059 -1.81304,-0.0271 -1.89422,-0.35178 -0.0812,-0.32472 ' + - '1.36925,-0.12989 1.75892,-0.64945 0.60885,-0.81181 1.3800713,-0.6765 1.8671505,' + - '-1.1094696 0.4870902,-0.4329599 1.0824089,-2.0836399 1.1906589,-2.7871996 0.108241,' + - '-0.70357 -1.0824084,-1.51538 -1.4071389,-2.05658 -0.3247195,-0.54121 0.7035702,' + - '-0.92005 3.1931099,-1.94834 2.48954,-1.02829 10.39114,-3.30134994 10.49938,' + - '-3.03074994 0.10824,0.27061 -2.59779,1.40713994 -4.492,2.11069994 -1.89422,0.70357 ' + - '-4.97909,2.05658 -4.97909,2.43542 0,0.37885 0.16236,0.67651 0.0541,1.54244 -0.10824,' + - '0.86593 -0.12123,1.2702597 -0.32472,1.8400997 -0.1353,0.37884 -0.2706,1.27183 ' + - '0,2.0836295 0.21648,0.64945 0.92005,1.13653 1.24477,1.24478 0.2706,0.018 1.01746,' + - '0.0433 1.8401,0 1.02829,-0.0541 2.48954,0.0541 2.48954,0.32472 0,0.2706 -2.21894,' + - '0.10824 -2.21894,0.48708 0,0.37885 2.27306,-0.0541 2.21894,0.32473 -0.0541,0.37884 ' + - '-1.89422,0.21648 -2.86839,0.21648 -0.77933,0 -1.93031,-0.0361 -2.43542,-0.21648 ' + - 'l -0.10824,0.37884 c -0.18038,0 -0.55744,0.10824 -0.94711,0.10824 -0.48708,0 ' + - '-0.51414,0.16236 -1.40713,0.16236 -0.892989,0 -0.622391,-0.0541 -1.4341894,-0.10824 ' + - '-0.81181,-0.0541 -3.842561,2.27306 -4.383761,3.03075 -0.54121,0.75768 ' + - '-0.21649,2.59778 -0.21649,3.43665 0,0.75379 -0.10824,2.43542 0,3.30135 z'; diff --git a/src/camera-manager/motioneye/assets/motioneye.svg b/src/camera-manager/motioneye/assets/motioneye.svg new file mode 100644 index 00000000..57c10b9c --- /dev/null +++ b/src/camera-manager/motioneye/assets/motioneye.svg @@ -0,0 +1,259 @@ + + + +image/svg+xml diff --git a/src/camera-manager/motioneye/engine-motioneye.ts b/src/camera-manager/motioneye/engine-motioneye.ts index e02553ab..49af14b2 100644 --- a/src/camera-manager/motioneye/engine-motioneye.ts +++ b/src/camera-manager/motioneye/engine-motioneye.ts @@ -41,7 +41,7 @@ import { QueryResultsType, QueryReturnType, } from '../types'; -import motioneyeLogo from './assets/motioneye-logo.svg'; +import motioneyeLogo from './assets/motioneye.svg'; import { MotionEyeEventQueryResults } from './types'; class MotionEyeQueryResultsClassifier { diff --git a/src/camera-manager/motioneye/icon.ts b/src/camera-manager/motioneye/icon.ts deleted file mode 100644 index f294c17e..00000000 --- a/src/camera-manager/motioneye/icon.ts +++ /dev/null @@ -1,51 +0,0 @@ -// Converted from https://raw.githubusercontent.com/motioneye-project/motioneye/python2/motioneye/static/img/motioneye-icon.svg . -export const MOTIONEYE_ICON_SVG_VIEWBOX = '0 0 64 64'; -export const MOTIONEYE_ICON_SVG_PATH = - 'M 49.65,10.81 ' + - 'C 44.24,10.84 36.85,13.50 31.48,15.96 ' + - '25.84,13.92 20.04,10.69 13.50,10.84 ' + - '13.07,10.85 12.65,10.87 12.20,10.91 ' + - '12.20,10.91 7.08,11.33 7.08,11.33 ' + - '7.08,11.33 11.94,12.95 11.94,12.95 ' + - '18.62,15.13 24.49,16.51 29.66,25.48 ' + - '30.86,25.48 33.22,25.48 34.34,25.48 ' + - '39.49,16.57 45.66,15.08 52.02,12.95 ' + - '52.02,12.95 56.83,11.39 56.83,11.39 ' + - '56.83,11.39 51.83,10.91 51.83,10.91 ' + - '51.15,10.84 50.43,10.80 49.65,10.81 ' + - '49.65,10.81 49.65,10.81 49.65,10.81 Z ' + - 'M 32.00,5.00 ' + - 'C 26.53,5.00 21.45,6.75 17.20,9.54 ' + - '21.80,10.04 26.33,11.22 31.48,13.76 ' + - '36.69,11.11 42.02,10.00 46.83,9.45 ' + - '42.57,6.64 37.48,5.00 32.00,5.00 Z ' + - 'M 43.42,22.65 ' + - 'C 41.70,22.65 40.31,24.05 40.31,25.77 ' + - '40.31,27.49 41.70,28.88 43.42,28.88 ' + - '45.14,28.88 46.54,27.49 46.54,25.77 ' + - '46.54,24.05 45.14,22.65 43.42,22.65 Z ' + - 'M 20.58,22.65 ' + - 'C 18.86,22.65 17.46,24.05 17.46,25.77 ' + - '17.46,27.49 18.86,28.88 20.58,28.88 ' + - '22.30,28.88 23.69,27.49 23.69,25.77 ' + - '23.69,24.05 22.30,22.65 20.58,22.65 Z ' + - 'M 11.91,14.02 ' + - 'C 7.61,18.80 5.00,25.06 5.00,32.00 ' + - '5.00,46.91 17.09,59.00 32.00,59.00 ' + - '46.91,59.00 59.00,46.91 59.00,32.00 ' + - '59.00,25.09 56.40,18.80 52.12,14.02 ' + - '50.08,14.77 48.04,15.65 46.02,16.78 ' + - '49.92,17.91 52.77,21.53 52.77,25.77 ' + - '52.77,30.90 48.59,35.12 43.42,35.12 ' + - '39.04,35.12 35.36,32.09 34.34,28.04 ' + - '34.34,28.04 29.66,28.04 29.66,28.04 ' + - '28.65,32.09 24.96,35.12 20.58,35.12 ' + - '15.41,35.12 11.20,30.90 11.20,25.77 ' + - '11.20,21.48 14.16,17.83 18.14,16.75 ' + - '16.12,15.65 14.04,14.79 11.91,14.02 ' + - '11.91,14.02 11.91,14.02 11.91,14.02 Z ' + - 'M 32.00,30.96 ' + - 'C 32.64,33.35 33.33,35.72 36.15,37.19 ' + - '36.15,37.19 32.00,43.42 32.00,43.42 ' + - '32.00,43.42 27.85,37.19 27.85,37.19 ' + - '30.32,35.44 31.46,33.29 32.00,30.96 Z'; diff --git a/src/components-lib/menu-controller.ts b/src/components-lib/menu-controller.ts index c621cf44..9e93d9fa 100644 --- a/src/components-lib/menu-controller.ts +++ b/src/components-lib/menu-controller.ts @@ -1,7 +1,6 @@ import { HASSDomEvent, HomeAssistant } from '@dermotduffy/custom-card-helpers'; import { LitElement } from 'lit'; import { orderBy } from 'lodash-es'; -import { FRIGATE_ICON_SVG_PATH } from '../camera-manager/frigate/icon.js'; import { dispatchActionExecutionRequest } from '../card-controller/actions/utils/execution-request.js'; import { FRIGATE_MENU_PRIORITY_MAX, @@ -10,7 +9,6 @@ import { type MenuConfig, type MenuItem, } from '../config/types.js'; -import { FRIGATE_BUTTON_MENU_ICON } from '../const.js'; import { StateParameters } from '../types.js'; import { convertActionToCardCustomAction, @@ -167,10 +165,6 @@ export class MenuController { : stateParameters; } - public getSVGPath(button: MenuItem): string { - return button.icon === FRIGATE_BUTTON_MENU_ICON ? FRIGATE_ICON_SVG_PATH : ''; - } - protected _sortButtons(): void { this._buttons = orderBy( this._buttons, diff --git a/src/components/menu.ts b/src/components/menu.ts index 4ced31bb..dc20faf8 100644 --- a/src/components/menu.ts +++ b/src/components/menu.ts @@ -8,6 +8,7 @@ import { MenuController } from '../components-lib/menu-controller.js'; import type { MenuConfig, MenuItem } from '../config/types.js'; import menuStyle from '../scss/menu.scss'; import { frigateCardHasAction } from '../utils/action.js'; +import { getCustomIconURL } from '../utils/custom-icons.js'; import { EntityRegistryManager } from '../utils/ha/registry/entity/index.js'; import './submenu.js'; @@ -72,7 +73,7 @@ export class FrigateCardMenu extends LitElement { // (`data-state`). This looks up a CSS style in `menu.scss`. const buttonState = this._controller.getFreshButtonState(this.hass, button); - const svgPath = this._controller.getSVGPath(button); + const customImageURL = getCustomIconURL(buttonState.icon); return html` this._controller.actionHandler(ev, button)} > - ${svgPath - ? html`` + ${customImageURL + ? html`` : html``} diff --git a/src/components/status-bar.ts b/src/components/status-bar.ts index 7cfcc988..3948c1be 100644 --- a/src/components/status-bar.ts +++ b/src/components/status-bar.ts @@ -13,6 +13,7 @@ import { StatusBarController } from '../components-lib/status-bar-controller'; import { StatusBarConfig, StatusBarItem } from '../config/types'; import statusStyle from '../scss/status.scss'; import { frigateCardHasAction } from '../utils/action'; +import { getCustomIconURL } from '../utils/custom-icons.js'; @customElement('frigate-card-status-bar') export class FrigateCardStatusBar extends LitElement { @@ -67,12 +68,20 @@ export class FrigateCardStatusBar extends LitElement { ${item.string} `; } else if (item.type === 'custom:frigate-card-status-bar-icon') { - return html` this._controller.actionHandler(ev, item.actions)} - >`; + const customIconURL = getCustomIconURL(item.icon); + return customIconURL + ? html` this._controller.actionHandler(ev, item.actions)} + />` + : html` this._controller.actionHandler(ev, item.actions)} + >`; } else if (item.type === 'custom:frigate-card-status-bar-image') { return html` { if (stateParameters.icon) { - return html` - `; + const url = getCustomIconURL(stateParameters.icon); + return url + ? html`` + : html` + `; } return html``; }; diff --git a/src/editor.ts b/src/editor.ts index 867a11b5..a29e7968 100644 --- a/src/editor.ts +++ b/src/editor.ts @@ -6,11 +6,6 @@ import { import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; -import { FRIGATE_ICON_SVG_PATH } from './camera-manager/frigate/icon.js'; -import { - MOTIONEYE_ICON_SVG_PATH, - MOTIONEYE_ICON_SVG_VIEWBOX, -} from './camera-manager/motioneye/icon.js'; import './components/key-assigner.js'; import { KeyboardShortcut } from './config/keyboard-shortcuts.js'; import { @@ -230,6 +225,7 @@ import { localize } from './localize/localize.js'; import frigate_card_editor_style from './scss/editor.scss'; import { arrayMove, prettifyTitle } from './utils/basic.js'; import { getCameraID } from './utils/camera.js'; +import { getCustomIconURL } from './utils/custom-icons.js'; import { getEntitiesFromHASS, getEntityTitle, @@ -1100,7 +1096,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_VIEW_DEFAULT_RESET, true, `config.${CONF_VIEW_DEFAULT_RESET}.editor_label`, - { name: 'mdi:restart' }, + 'mdi:restart', html` ${this._renderSwitch( CONF_VIEW_DEFAULT_RESET_AFTER_INTERACTION, @@ -1130,7 +1126,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_VIEW_TRIGGERS, true, `config.${CONF_VIEW_TRIGGERS}.editor_label`, - { name: 'mdi:target-account' }, + 'mdi:target-account', html` ${this._renderSwitch( CONF_VIEW_TRIGGERS_FILTER_SELECTED_CAMERA, @@ -1153,7 +1149,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_VIEW_TRIGGERS_ACTIONS, true, `config.${CONF_VIEW_TRIGGERS_ACTIONS}.editor_label`, - { name: 'mdi:cogs' }, + 'mdi:cogs', html` ${this._renderOptionSelector( CONF_VIEW_TRIGGERS_ACTIONS_TRIGGER, this._triggersActionsTrigger, @@ -1198,7 +1194,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_VIEW_KEYBOARD_SHORTCUTS, true, `config.${CONF_VIEW_KEYBOARD_SHORTCUTS}.editor_label`, - { name: 'mdi:keyboard' }, + 'mdi:keyboard', html` ${this._renderSwitch( CONF_VIEW_KEYBOARD_SHORTCUTS_ENABLED, @@ -1244,7 +1240,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_STATUS_BAR_ITEMS, item, `config.status_bar.items.${item}`, - { name: 'mdi:feature-search' }, + 'mdi:feature-search', html` ${this._renderSwitch( `${CONF_STATUS_BAR_ITEMS}.${item}.enabled`, @@ -1276,7 +1272,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_MENU_BUTTONS, button, `config.menu.buttons.${button}`, - { name: 'mdi:gesture-tap-button' }, + 'mdi:gesture-tap-button', html` ${this._renderSwitch( `${CONF_MENU_BUTTONS}.${button}.enabled`, @@ -1325,11 +1321,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor domain: string, key: unknown, labelPath: string, - icon: { - name?: string; - path?: string; - viewBox?: string; - }, + icon: string, template: TemplateResult, ): TemplateResult { const selected = this._expandedMenus[domain] === key; @@ -1338,6 +1330,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor selected: selected, }; + const customIconURL = getCustomIconURL(icon); return html`
${selected ? html`
${template}
` : ''} @@ -1385,7 +1374,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor domain, true, labelPath, - { name: 'mdi:page-layout-body' }, + 'mdi:page-layout-body', html` ${this._renderNumberInput(configPathZoom, { min: 0, @@ -1410,7 +1399,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor `${domain}.position`, true, 'config.cameras.dimensions.layout.position.editor_label', - { name: 'mdi:aspect-ratio' }, + 'mdi:aspect-ratio', html` ${this._renderNumberInput(configPathPositionX, { min: 0, max: 100, @@ -1426,7 +1415,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor `${domain}.view_box`, true, 'config.cameras.dimensions.layout.view_box.editor_label', - { name: 'mdi:crop' }, + 'mdi:crop', html` ${this._renderNumberInput(configPathViewBoxTop, { min: 0, @@ -1525,7 +1514,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor domain, true, 'config.common.controls.timeline.editor_label', - { name: 'mdi:chart-gantt' }, + 'mdi:chart-gantt', html` ${this._renderOptionSelector(configPathMode, this._miniTimelineModes, { label: localize('config.common.controls.timeline.mode'), })} @@ -1563,7 +1552,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor domain, true, 'config.common.display.editor_label', - { name: 'mdi:palette-swatch' }, + 'mdi:palette-swatch', html` ${this._renderOptionSelector(configPathMode, this._displayModes, { label: localize('config.common.display.mode'), @@ -1604,7 +1593,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor domain, true, 'config.common.controls.next_previous.editor_label', - { name: 'mdi:arrow-right-bold-circle' }, + 'mdi:arrow-right-bold-circle', html` ${this._renderOptionSelector( configPathStyle, @@ -1659,7 +1648,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor domain, true, 'config.common.controls.thumbnails.editor_label', - { name: 'mdi:image-text' }, + 'mdi:image-text', html` ${options?.configPathMode ? html`${this._renderOptionSelector( @@ -1735,7 +1724,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor domain, true, 'config.common.controls.filter.editor_label', - { name: 'mdi:filter-cog' }, + 'mdi:filter-cog', html` ${configPathMode ? html`${this._renderOptionSelector(configPathMode, this._filterModes, { @@ -1946,12 +1935,12 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_CAMERAS_ENGINE, true, 'config.cameras.engines.editor_label', - { name: 'mdi:engine' }, + 'mdi:engine', html`${this._putInSubmenu( MENU_CAMERAS_FRIGATE, cameraIndex, 'config.cameras.frigate.editor_label', - { path: FRIGATE_ICON_SVG_PATH }, + 'frigate', html` ${this._renderStringInput( getArrayConfigPath( @@ -1990,7 +1979,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_CAMERAS_MOTIONEYE, cameraIndex, 'config.cameras.motioneye.editor_label', - { path: MOTIONEYE_ICON_SVG_PATH, viewBox: MOTIONEYE_ICON_SVG_VIEWBOX }, + 'motioneye', html` ${this._renderStringInput( getArrayConfigPath(CONF_CAMERAS_ARRAY_MOTIONEYE_URL, cameraIndex), @@ -2026,12 +2015,12 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_CAMERAS_LIVE_PROVIDER, true, 'config.cameras.live_provider_options.editor_label', - { name: 'mdi:cctv' }, + 'mdi:cctv', html` ${this._putInSubmenu( MENU_CAMERAS_GO2RTC, cameraIndex, 'config.cameras.go2rtc.editor_label', - { name: 'mdi:alpha-g-circle' }, + 'mdi:alpha-g-circle', html`${this._renderOptionSelector( getArrayConfigPath(CONF_CAMERAS_ARRAY_GO2RTC_MODES, cameraIndex), this._go2rtcModes, @@ -2048,7 +2037,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_CAMERAS_IMAGE, true, 'config.cameras.image.editor_label', - { name: 'mdi:image' }, + 'mdi:image', this._renderImageOptions( getArrayConfigPath(CONF_CAMERAS_ARRAY_IMAGE_MODE, cameraIndex), getArrayConfigPath(CONF_CAMERAS_ARRAY_IMAGE_URL, cameraIndex), @@ -2067,7 +2056,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_CAMERAS_WEBRTC_CARD, cameraIndex, 'config.cameras.webrtc_card.editor_label', - { name: 'mdi:webrtc' }, + 'mdi:webrtc', html`${this._renderEntitySelector( getArrayConfigPath( CONF_CAMERAS_ARRAY_WEBRTC_CARD_ENTITY, @@ -2084,7 +2073,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_CAMERAS_DEPENDENCIES, cameraIndex, 'config.cameras.dependencies.editor_label', - { name: 'mdi:graph' }, + 'mdi:graph', html` ${this._renderSwitch( getArrayConfigPath( CONF_CAMERAS_ARRAY_DEPENDENCIES_ALL_CAMERAS, @@ -2107,7 +2096,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_CAMERAS_TRIGGERS, cameraIndex, 'config.cameras.triggers.editor_label', - { name: 'mdi:magnify-scan' }, + 'mdi:magnify-scan', html` ${this._renderSwitch( getArrayConfigPath( @@ -2144,7 +2133,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_CAMERAS_CAST, cameraIndex, 'config.cameras.cast.editor_label', - { name: 'mdi:cast' }, + 'mdi:cast', html` ${this._renderOptionSelector( getArrayConfigPath(CONF_CAMERAS_ARRAY_CAST_METHOD, cameraIndex), @@ -2168,7 +2157,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_CAMERAS_DIMENSIONS, cameraIndex, 'config.cameras.dimensions.editor_label', - { name: 'mdi:aspect-ratio' }, + 'mdi:aspect-ratio', html` ${this._renderStringInput( getArrayConfigPath( @@ -2226,7 +2215,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_CAMERAS_CAPABILITIES, cameraIndex, 'config.cameras.capabilities.editor_label', - { name: 'mdi:cog-stop' }, + 'mdi:cog-stop', html` ${this._renderOptionSelector( getArrayConfigPath( @@ -2538,7 +2527,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_LIVE_CONTROLS, true, 'config.live.controls.editor_label', - { name: 'mdi:gamepad' }, + 'mdi:gamepad', html` ${this._renderSwitch( CONF_LIVE_CONTROLS_BUILTIN, @@ -2585,7 +2574,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_LIVE_CONTROLS_PTZ, true, 'config.live.controls.ptz.editor_label', - { name: 'mdi:pan' }, + 'mdi:pan', html` ${this._renderOptionSelector( CONF_LIVE_CONTROLS_PTZ_MODE, @@ -2628,7 +2617,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_LIVE_MICROPHONE, true, 'config.live.microphone.editor_label', - { name: 'mdi:microphone' }, + 'mdi:microphone', html` ${this._renderNumberInput(CONF_LIVE_MICROPHONE_DISCONNECT_SECONDS)} ${this._renderSwitch( @@ -2723,7 +2712,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_MEDIA_VIEWER_CONTROLS, true, 'config.media_viewer.controls.editor_label', - { name: 'mdi:gamepad' }, + 'mdi:gamepad', html` ${this._renderSwitch( CONF_MEDIA_VIEWER_CONTROLS_BUILTIN, @@ -2830,7 +2819,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_PERFORMANCE_FEATURES, true, 'config.performance.features.editor_label', - { name: 'mdi:feature-search' }, + 'mdi:feature-search', html` ${this._renderSwitch( CONF_PERFORMANCE_FEATURES_ANIMATED_PROGRESS_INDICATOR, @@ -2851,7 +2840,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor MENU_PERFORMANCE_STYLE, true, 'config.performance.style.editor_label', - { name: 'mdi:palette-swatch-variant' }, + 'mdi:palette-swatch-variant', html` ${this._renderSwitch( CONF_PERFORMANCE_STYLE_BORDER_RADIUS, diff --git a/src/scss/editor.scss b/src/scss/editor.scss index cbce8562..a7139ec9 100644 --- a/src/scss/editor.scss +++ b/src/scss/editor.scss @@ -71,7 +71,7 @@ div.upgrade span { } .submenu-header ha-icon, -.submenu-header ha-svg-icon { +.submenu-header img { padding-right: 15px; } diff --git a/src/utils/custom-icons.ts b/src/utils/custom-icons.ts new file mode 100644 index 00000000..92514686 --- /dev/null +++ b/src/utils/custom-icons.ts @@ -0,0 +1,13 @@ +import frigateSVG from '../camera-manager/frigate/assets/frigate.svg'; +import motioneyeSVG from '../camera-manager/motioneye/assets/motioneye.svg'; + +export const getCustomIconURL = (icon?: string): string | null => { + switch (icon) { + case 'frigate': + return frigateSVG; + case 'motioneye': + return motioneyeSVG; + default: + return null; + } +}; diff --git a/src/utils/ha/index.ts b/src/utils/ha/index.ts index 6a15b77f..bb505f29 100644 --- a/src/utils/ha/index.ts +++ b/src/utils/ha/index.ts @@ -306,7 +306,6 @@ export const sideLoadHomeAssistantElements = async (): Promise => { 'ha-icon', 'ha-menu-button', 'ha-selector', - 'ha-svg-icon', 'ha-web-rtc-player', ]; diff --git a/tests/components-lib/menu-controller.test.ts b/tests/components-lib/menu-controller.test.ts index e2bd5c79..01fd06e2 100644 --- a/tests/components-lib/menu-controller.test.ts +++ b/tests/components-lib/menu-controller.test.ts @@ -1,6 +1,5 @@ import { handleActionConfig } from '@dermotduffy/custom-card-helpers'; import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { FRIGATE_ICON_SVG_PATH } from '../../src/camera-manager/frigate/icon'; import { MenuController } from '../../src/components-lib/menu-controller'; import { MenuConfig, menuConfigSchema } from '../../src/config/types'; import { StateParameters } from '../../src/types'; @@ -395,26 +394,6 @@ describe('MenuController', () => { }); }); - describe('should get svg path', () => { - it('frigate icon', () => { - const controller = new MenuController(createLitElement()); - const button = { - type: 'custom:frigate-card-menu-icon' as const, - icon: 'frigate', - }; - expect(controller.getSVGPath(button)).toEqual(FRIGATE_ICON_SVG_PATH); - }); - - it('non-frigate icon', () => { - const controller = new MenuController(createLitElement()); - const button = { - type: 'custom:frigate-card-menu-icon' as const, - icon: 'mdi:cow', - }; - expect(controller.getSVGPath(button)).toBeFalsy(); - }); - }); - describe('should handle actions', () => { it('should bail without config', () => { const controller = new MenuController(createLitElement()); diff --git a/tests/utils/custom-icons.test.ts b/tests/utils/custom-icons.test.ts new file mode 100644 index 00000000..1bc10d9d --- /dev/null +++ b/tests/utils/custom-icons.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from 'vitest'; +import frigateSVG from '../../src/camera-manager/frigate/assets/frigate.svg'; +import motioneyeSVG from '../../src/camera-manager/motioneye/assets/motioneye.svg'; +import { getCustomIconURL } from '../../src/utils/custom-icons'; + +describe('getCustomIconURL', () => { + it('should return frigate SVG for frigate icon', () => { + expect(getCustomIconURL('frigate')).toBe(frigateSVG); + }); + + it('should return motioneye SVG for motioneye icon', () => { + expect(getCustomIconURL('motioneye')).toBe(motioneyeSVG); + }); + + it('should return null for mdi icon', () => { + expect(getCustomIconURL('mdi:car')).toBeNull(); + }); + + it('should return null for undefined icon', () => { + expect(getCustomIconURL()).toBeNull(); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 879e97ef..c8c9094e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -31,7 +31,6 @@ "ha-icon-button", "ha-icon", "ha-selector", - "ha-svg-icon", "mwc-button", "mwc-list-item" ], diff --git a/vite.config.ts b/vite.config.ts index 78d0127b..2d1a74a6 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -5,11 +5,9 @@ const FULL_COVERAGE_FILES_RELATIVE = [ 'camera-manager/*.ts', 'camera-manager/browse-media/camera.ts', 'camera-manager/frigate/camera.ts', - 'camera-manager/frigate/icon.ts', 'camera-manager/frigate/requests.ts', 'camera-manager/frigate/util.ts', 'camera-manager/generic/*.ts', - 'camera-manager/motioneye/icon.ts', 'camera-manager/utils/*.ts', 'card-controller/**/*.ts', 'components-lib/**/!(timeline-source.ts)', @@ -20,6 +18,7 @@ const FULL_COVERAGE_FILES_RELATIVE = [ 'utils/audio.ts', 'utils/basic.ts', 'utils/camera.ts', + 'utils/custom-icons.ts', 'utils/debug.ts', 'utils/diagnostics.ts', 'utils/download.ts',