From 54a2b2534aa74b2d43cce6aa192ef55bce1a21b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 24 Feb 2021 17:05:42 +0100 Subject: [PATCH] Add add-on selector/picker (#8422) --- src/components/ha-addon-picker.ts | 148 ++++++++++++++++++ .../ha-selector/ha-selector-addon.ts | 30 ++++ src/components/ha-selector/ha-selector.ts | 1 + src/data/selector.ts | 8 + src/translations/en.json | 13 ++ 5 files changed, 200 insertions(+) create mode 100644 src/components/ha-addon-picker.ts create mode 100644 src/components/ha-selector/ha-selector-addon.ts diff --git a/src/components/ha-addon-picker.ts b/src/components/ha-addon-picker.ts new file mode 100644 index 0000000000..582eaefcff --- /dev/null +++ b/src/components/ha-addon-picker.ts @@ -0,0 +1,148 @@ +import { + customElement, + html, + internalProperty, + LitElement, + property, + query, + TemplateResult, +} from "lit-element"; +import { isComponentLoaded } from "../common/config/is_component_loaded"; +import { fireEvent } from "../common/dom/fire_event"; +import { compare } from "../common/string/compare"; +import { HassioAddonInfo } from "../data/hassio/addon"; +import { fetchHassioSupervisorInfo } from "../data/hassio/supervisor"; +import { showAlertDialog } from "../dialogs/generic/show-dialog-box"; +import { PolymerChangedEvent } from "../polymer-types"; +import { HomeAssistant } from "../types"; +import { HaComboBox } from "./ha-combo-box"; + +const rowRenderer = ( + root: HTMLElement, + _owner, + model: { item: HassioAddonInfo } +) => { + if (!root.firstElementChild) { + root.innerHTML = ` + + + +
[[item.name]]
+
[[item.slug]]
+
+
+ `; + } + + root.querySelector(".name")!.textContent = model.item.name; + root.querySelector("[secondary]")!.textContent = model.item.slug; +}; + +@customElement("ha-addon-picker") +class HaAddonPicker extends LitElement { + public hass!: HomeAssistant; + + @property() public label?: string; + + @property() public value = ""; + + @internalProperty() private _addons?: HassioAddonInfo[]; + + @property({ type: Boolean }) public disabled = false; + + @query("ha-combo-box") private _comboBox!: HaComboBox; + + public open() { + this._comboBox?.open(); + } + + public focus() { + this._comboBox?.focus(); + } + + protected firstUpdated() { + this._getAddons(); + } + + protected render(): TemplateResult { + if (!this._addons) { + return html``; + } + return html` + + `; + } + + private async _getAddons() { + try { + if (isComponentLoaded(this.hass, "hassio")) { + const supervisorInfo = await fetchHassioSupervisorInfo(this.hass); + this._addons = supervisorInfo.addons.sort((a, b) => + compare(a.name, b.name) + ); + } else { + showAlertDialog(this, { + title: this.hass.localize( + "ui.componencts.addon-picker.error.no_supervisor.title" + ), + text: this.hass.localize( + "ui.componencts.addon-picker.error.no_supervisor.description" + ), + }); + } + } catch (error) { + showAlertDialog(this, { + title: this.hass.localize( + "ui.componencts.addon-picker.error.fetch_addons.title" + ), + text: this.hass.localize( + "ui.componencts.addon-picker.error.fetch_addons.description" + ), + }); + } + } + + private get _value() { + return this.value || ""; + } + + private _addonChanged(ev: PolymerChangedEvent) { + ev.stopPropagation(); + const newValue = ev.detail.value; + + if (newValue !== this._value) { + this._setValue(newValue); + } + } + + private _setValue(value: string) { + this.value = value; + setTimeout(() => { + fireEvent(this, "value-changed", { value }); + fireEvent(this, "change"); + }, 0); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-addon-picker": HaAddonPicker; + } +} diff --git a/src/components/ha-selector/ha-selector-addon.ts b/src/components/ha-selector/ha-selector-addon.ts new file mode 100644 index 0000000000..923fb4c29e --- /dev/null +++ b/src/components/ha-selector/ha-selector-addon.ts @@ -0,0 +1,30 @@ +import { customElement, html, LitElement, property } from "lit-element"; +import { AddonSelector } from "../../data/selector"; +import { HomeAssistant } from "../../types"; +import "../ha-addon-picker"; + +@customElement("ha-selector-addon") +export class HaAddonSelector extends LitElement { + @property() public hass!: HomeAssistant; + + @property() public selector!: AddonSelector; + + @property() public value?: any; + + @property() public label?: string; + + protected render() { + return html``; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-selector-addon": HaAddonSelector; + } +} diff --git a/src/components/ha-selector/ha-selector.ts b/src/components/ha-selector/ha-selector.ts index db071febdc..9982e33831 100644 --- a/src/components/ha-selector/ha-selector.ts +++ b/src/components/ha-selector/ha-selector.ts @@ -3,6 +3,7 @@ import { dynamicElement } from "../../common/dom/dynamic-element-directive"; import { Selector } from "../../data/selector"; import { HomeAssistant } from "../../types"; import "./ha-selector-action"; +import "./ha-selector-addon"; import "./ha-selector-area"; import "./ha-selector-boolean"; import "./ha-selector-device"; diff --git a/src/data/selector.ts b/src/data/selector.ts index a3eedee92d..7722d56de8 100644 --- a/src/data/selector.ts +++ b/src/data/selector.ts @@ -1,4 +1,5 @@ export type Selector = + | AddonSelector | EntitySelector | DeviceSelector | AreaSelector @@ -30,6 +31,13 @@ export interface DeviceSelector { }; } +export interface AddonSelector { + addon: { + name?: string; + slug?: string; + }; +} + export interface AreaSelector { area: { entity?: { diff --git a/src/translations/en.json b/src/translations/en.json index 48142683c9..f252cf1056 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -382,6 +382,19 @@ "failed_create_area": "Failed to create area." } }, + "addon-picker": { + "addon": "Add-on", + "error": { + "no_supervisor": { + "title": "No Supervisor", + "description": "No Supervisor found, so add-ons could not be loaded." + }, + "fetch_addons": { + "title": "Error fetching add-ons", + "description": "Fetching add-ons returned an error." + } + } + }, "picture-upload": { "label": "Picture", "unsupported_format": "Unsupported format, please choose a JPEG, PNG or GIF image."