diff --git a/hassio/src/addon-view/config/hassio-addon-network.ts b/hassio/src/addon-view/config/hassio-addon-network.ts index d90a68ffd3..99f871be99 100644 --- a/hassio/src/addon-view/config/hassio-addon-network.ts +++ b/hassio/src/addon-view/config/hassio-addon-network.ts @@ -88,8 +88,8 @@ class HassioAddonNetwork extends LitElement {
- Reset to defaults > + Reset to defaults + Save diff --git a/hassio/src/dashboard/hassio-update.ts b/hassio/src/dashboard/hassio-update.ts index 3bb15444ed..9bb6cc73ec 100644 --- a/hassio/src/dashboard/hassio-update.ts +++ b/hassio/src/dashboard/hassio-update.ts @@ -164,8 +164,9 @@ export class HassioUpdate extends LitElement { try { await this.hass.callApi>("POST", item.apiPath); } catch (err) { - // Only show an error if the status code was not 504, or no status at all (connection terminated) - if (err.status_code && err.status_code !== 504) { + // Only show an error if the status code was not expected (user behind proxy) + // or no status at all(connection terminated) + if (err.status_code && ![502, 503, 504].includes(err.status_code)) { showAlertDialog(this, { title: "Update failed", text: extractApiErrorMessage(err), diff --git a/hassio/src/system/hassio-supervisor-info.ts b/hassio/src/system/hassio-supervisor-info.ts index fd043751de..12af46d091 100644 --- a/hassio/src/system/hassio-supervisor-info.ts +++ b/hassio/src/system/hassio-supervisor-info.ts @@ -18,6 +18,7 @@ import { setSupervisorOption, SupervisorOptions, updateSupervisor, + fetchHassioSupervisorInfo, } from "../../../src/data/hassio/supervisor"; import { showAlertDialog, @@ -176,10 +177,11 @@ class HassioSupervisorInfo extends LitElement { try { const data: Partial = { - channel: this.supervisorInfo.channel !== "stable" ? "beta" : "stable", + channel: this.supervisorInfo.channel === "stable" ? "beta" : "stable", }; await setSupervisorOption(this.hass, data); await reloadSupervisor(this.hass); + this.supervisorInfo = await fetchHassioSupervisorInfo(this.hass); } catch (err) { showAlertDialog(this, { title: "Failed to set supervisor option", @@ -195,6 +197,7 @@ class HassioSupervisorInfo extends LitElement { try { await reloadSupervisor(this.hass); + this.supervisorInfo = await fetchHassioSupervisorInfo(this.hass); } catch (err) { showAlertDialog(this, { title: "Failed to reload the supervisor", diff --git a/package.json b/package.json index 1410d6bf3b..1db467294c 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "@polymer/polymer": "3.1.0", "@thomasloven/round-slider": "0.5.0", "@types/chromecast-caf-sender": "^1.0.3", + "@types/sortablejs": "^1.10.6", "@vaadin/vaadin-combo-box": "^5.0.10", "@vaadin/vaadin-date-picker": "^4.0.7", "@vue/web-component-wrapper": "^1.2.0", diff --git a/setup.py b/setup.py index 4c386522cb..b1a1ef7a73 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20200904.0", + version="20200907.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/home-assistant-polymer", author="The Home Assistant Authors", diff --git a/src/components/entity/ha-entity-attribute-picker.ts b/src/components/entity/ha-entity-attribute-picker.ts new file mode 100644 index 0000000000..ee2958323b --- /dev/null +++ b/src/components/entity/ha-entity-attribute-picker.ts @@ -0,0 +1,178 @@ +import "@polymer/paper-input/paper-input"; +import "@polymer/paper-item/paper-item"; +import "@vaadin/vaadin-combo-box/theme/material/vaadin-combo-box-light"; +import { HassEntity } from "home-assistant-js-websocket"; +import { + css, + CSSResult, + customElement, + html, + LitElement, + property, + PropertyValues, + query, + TemplateResult, +} from "lit-element"; +import { fireEvent } from "../../common/dom/fire_event"; +import { PolymerChangedEvent } from "../../polymer-types"; +import { HomeAssistant } from "../../types"; +import "../ha-icon-button"; +import "./state-badge"; + +export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean; + +const rowRenderer = (root: HTMLElement, _owner, model: { item: string }) => { + if (!root.firstElementChild) { + root.innerHTML = ` + + + `; + } + root.querySelector("paper-item")!.textContent = model.item; +}; + +@customElement("ha-entity-attribute-picker") +class HaEntityAttributePicker extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public entityId?: string; + + @property({ type: Boolean }) public autofocus = false; + + @property({ type: Boolean }) public disabled = false; + + @property({ type: Boolean, attribute: "allow-custom-value" }) + public allowCustomValue; + + @property() public label?: string; + + @property() public value?: string; + + @property({ type: Boolean }) private _opened = false; + + @query("vaadin-combo-box-light") private _comboBox!: HTMLElement; + + protected shouldUpdate(changedProps: PropertyValues) { + return !(!changedProps.has("_opened") && this._opened); + } + + protected updated(changedProps: PropertyValues) { + if (changedProps.has("_opened") && this._opened) { + const state = this.entityId ? this.hass.states[this.entityId] : undefined; + (this._comboBox as any).items = state + ? Object.keys(state.attributes) + : []; + } + } + + protected render(): TemplateResult { + if (!this.hass) { + return html``; + } + + return html` + + + ${this.value + ? html` + + Clear + + ` + : ""} + + + Toggle + + + + `; + } + + private _clearValue(ev: Event) { + ev.stopPropagation(); + this._setValue(""); + } + + private get _value() { + return this.value || ""; + } + + private _openedChanged(ev: PolymerChangedEvent) { + this._opened = ev.detail.value; + } + + private _valueChanged(ev: PolymerChangedEvent) { + 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); + } + + static get styles(): CSSResult { + return css` + paper-input > ha-icon-button { + --mdc-icon-button-size: 24px; + padding: 0px 2px; + color: var(--secondary-text-color); + } + [hidden] { + display: none; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-entity-attribute-picker": HaEntityAttributePicker; + } +} diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts index 300ae57a79..51ddf0d30f 100644 --- a/src/components/entity/ha-entity-picker.ts +++ b/src/components/entity/ha-entity-picker.ts @@ -1,4 +1,3 @@ -import "../ha-icon-button"; import "@polymer/paper-input/paper-input"; import "@polymer/paper-item/paper-icon-item"; import "@polymer/paper-item/paper-item-body"; @@ -20,6 +19,7 @@ import { computeDomain } from "../../common/entity/compute_domain"; import { computeStateName } from "../../common/entity/compute_state_name"; import { PolymerChangedEvent } from "../../polymer-types"; import { HomeAssistant } from "../../types"; +import "../ha-icon-button"; import "./state-badge"; export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean; @@ -95,6 +95,8 @@ class HaEntityPicker extends LitElement { @query("vaadin-combo-box-light") private _comboBox!: HTMLElement; + private _initedStates = false; + private _getStates = memoizeOne( ( _opened: boolean, @@ -148,11 +150,18 @@ class HaEntityPicker extends LitElement { ); protected shouldUpdate(changedProps: PropertyValues) { + if ( + changedProps.has("value") || + changedProps.has("label") || + changedProps.has("disabled") + ) { + return true; + } return !(!changedProps.has("_opened") && this._opened); } protected updated(changedProps: PropertyValues) { - if (changedProps.has("_opened") && this._opened) { + if (!this._initedStates || (changedProps.has("_opened") && this._opened)) { const states = this._getStates( this._opened, this.hass, @@ -162,6 +171,7 @@ class HaEntityPicker extends LitElement { this.includeDeviceClasses ); (this._comboBox as any).items = states; + this._initedStates = true; } } @@ -169,7 +179,6 @@ class HaEntityPicker extends LitElement { if (!this.hass) { return html``; } - return html` - #sortable a:nth-of-type(2n) paper-icon-item { - animation-name: keyframes1; - animation-iteration-count: infinite; - transform-origin: 50% 10%; - animation-delay: -0.75s; - animation-duration: 0.25s; - } - - #sortable a:nth-of-type(2n-1) paper-icon-item { - animation-name: keyframes2; - animation-iteration-count: infinite; - animation-direction: alternate; - transform-origin: 30% 5%; - animation-delay: -0.5s; - animation-duration: 0.33s; - } - - #sortable { - outline: none; - display: flex; - flex-direction: column; - } - - .sortable-ghost { - opacity: 0.4; - } - - .sortable-fallback { - opacity: 0; - } - - @keyframes keyframes1 { - 0% { - transform: rotate(-1deg); - animation-timing-function: ease-in; - } - - 50% { - transform: rotate(1.5deg); - animation-timing-function: ease-out; - } - } - - @keyframes keyframes2 { - 0% { - transform: rotate(1deg); - animation-timing-function: ease-in; - } - - 50% { - transform: rotate(-1.5deg); - animation-timing-function: ease-out; - } - } - - .hide-panel { - display: none; - position: absolute; - right: 8px; - } - - :host([expanded]) .hide-panel { - display: inline-flex; - } - - paper-icon-item.hidden-panel, - paper-icon-item.hidden-panel span, - paper-icon-item.hidden-panel ha-icon[slot="item-icon"] { - color: var(--secondary-text-color); - cursor: pointer; - } - -`; diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 12b1c17d39..1226fffbc4 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -23,7 +23,6 @@ import { LitElement, property, PropertyValues, - TemplateResult, } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; import { guard } from "lit-html/directives/guard"; @@ -31,6 +30,7 @@ import memoizeOne from "memoize-one"; import { LocalStorage } from "../common/decorators/local-storage"; import { fireEvent } from "../common/dom/fire_event"; import { computeDomain } from "../common/entity/compute_domain"; +import { navigate } from "../common/navigate"; import { compare } from "../common/string/compare"; import { computeRTL } from "../common/util/compute_rtl"; import { ActionHandlerDetail } from "../data/lovelace"; @@ -160,7 +160,7 @@ const computePanels = memoizeOne( let Sortable; -let sortStyles: TemplateResult; +let sortStyles: CSSResult; @customElement("ha-sidebar") class HaSidebar extends LitElement { @@ -228,7 +228,13 @@ class HaSidebar extends LitElement { } return html` - ${this._editMode ? sortStyles : ""} + ${this._editMode + ? html` + + ` + : ""}