From 5bfdc982171f725ef33df828331298dfbb4d1d06 Mon Sep 17 00:00:00 2001 From: Ian Richardson Date: Mon, 2 Sep 2019 00:23:37 -0500 Subject: [PATCH 01/43] Alarm codes (#3566) * Handle alarm codes from keyboard input Closes https://github.com/home-assistant/home-assistant-polymer/issues/2602 * remove friendly_name changes * remove unnecessary TS check --- src/panels/lovelace/cards/hui-alarm-panel-card.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/hui-alarm-panel-card.ts b/src/panels/lovelace/cards/hui-alarm-panel-card.ts index 899a2c6eb9..f9b3a2a1a1 100644 --- a/src/panels/lovelace/cards/hui-alarm-panel-card.ts +++ b/src/panels/lovelace/cards/hui-alarm-panel-card.ts @@ -21,6 +21,7 @@ import { FORMAT_NUMBER, } from "../../../data/alarm_control_panel"; import { AlarmPanelCardConfig } from "./types"; +import { PaperInputElement } from "@polymer/paper-input/paper-input"; const ICONS = { armed_away: "hass:shield-lock", @@ -144,6 +145,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard { ? html`` : html` 0 ? input.value : ""); callAlarmAction( this.hass!, this._config!.entity, (e.currentTarget! as any).action, - this._code! + code ); this._code = ""; } From a97ce49f0ba24c61ba2b41cb38d7fba7d2d8c985 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 2 Sep 2019 14:36:26 +0200 Subject: [PATCH 02/43] Update azure-pipelines-release.yml for Azure Pipelines --- azure-pipelines-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines-release.yml b/azure-pipelines-release.yml index f7b47f9813..5fceabc52a 100644 --- a/azure-pipelines-release.yml +++ b/azure-pipelines-release.yml @@ -8,7 +8,7 @@ trigger: pr: none variables: - name: versionWheels - value: '1.1-3.7-alpine3.10' + value: '1.3-3.7-alpine3.10' - name: versionNode value: '12.1' - group: twine From cf7a3006140f5269e3b66eb398c27f31219b7b0b Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 2 Sep 2019 18:10:48 +0200 Subject: [PATCH 03/43] Don't remove `hvac_action` from history attributes (#3570) So it can be used to plot a fill when active in the graph. --- src/data/history.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/history.ts b/src/data/history.ts index 3f2556522d..0497658031 100644 --- a/src/data/history.ts +++ b/src/data/history.ts @@ -11,6 +11,7 @@ const LINE_ATTRIBUTES_TO_KEEP = [ "current_temperature", "target_temp_low", "target_temp_high", + "hvac_action", ]; export interface LineChartState { From fcd206e94b21a59205f53bf5b3683d4eca70782e Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 3 Sep 2019 06:17:03 +0200 Subject: [PATCH 04/43] Update the map when making config changes (#3568) --- src/common/dom/setup-leaflet-map.ts | 17 +++++++++----- src/panels/lovelace/cards/hui-map-card.ts | 27 +++++++++++++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/common/dom/setup-leaflet-map.ts b/src/common/dom/setup-leaflet-map.ts index bbbb03a104..d0532721a6 100644 --- a/src/common/dom/setup-leaflet-map.ts +++ b/src/common/dom/setup-leaflet-map.ts @@ -20,10 +20,19 @@ export const setupLeafletMap = async ( style.setAttribute("rel", "stylesheet"); mapElement.parentNode.appendChild(style); map.setView([52.3731339, 4.8903147], 13); - Leaflet.tileLayer( + createTileLayer(Leaflet, darkMode).addTo(map); + + return [map, Leaflet]; +}; + +export const createTileLayer = ( + leaflet: LeafletModuleType, + darkMode: boolean +) => { + return leaflet.tileLayer( `https://{s}.basemaps.cartocdn.com/${ darkMode ? "dark_all" : "light_all" - }/{z}/{x}/{y}${Leaflet.Browser.retina ? "@2x.png" : ".png"}`, + }/{z}/{x}/{y}${leaflet.Browser.retina ? "@2x.png" : ".png"}`, { attribution: '© OpenStreetMap, © CARTO', @@ -31,7 +40,5 @@ export const setupLeafletMap = async ( minZoom: 0, maxZoom: 20, } - ).addTo(map); - - return [map, Leaflet]; + ); }; diff --git a/src/panels/lovelace/cards/hui-map-card.ts b/src/panels/lovelace/cards/hui-map-card.ts index 21541f48c8..2d706d4528 100644 --- a/src/panels/lovelace/cards/hui-map-card.ts +++ b/src/panels/lovelace/cards/hui-map-card.ts @@ -15,6 +15,7 @@ import "../../map/ha-entity-marker"; import { setupLeafletMap, + createTileLayer, LeafletModuleType, } from "../../../common/dom/setup-leaflet-map"; import computeStateDomain from "../../../common/entity/compute_state_domain"; @@ -194,6 +195,12 @@ class HuiMapCard extends LitElement implements LovelaceCard { if (changedProps.has("hass")) { this._drawEntities(); } + if ( + changedProps.has("_config") && + changedProps.get("_config") !== undefined + ) { + this.updateMap(changedProps.get("_config") as MapCardConfig); + } } private get _mapEl(): HTMLDivElement { @@ -210,6 +217,26 @@ class HuiMapCard extends LitElement implements LovelaceCard { this._fitMap(); } + private updateMap(oldConfig: MapCardConfig): void { + const map = this._leafletMap; + const config = this._config; + const Leaflet = this.Leaflet; + if (!map || !config || !Leaflet) { + return; + } + if (config.dark_mode !== oldConfig.dark_mode) { + createTileLayer(Leaflet, config.dark_mode === true).addTo(map); + } + if ( + config.entities !== oldConfig.entities || + config.geo_location_sources !== oldConfig.geo_location_sources + ) { + this._drawEntities(); + } + map.invalidateSize(); + this._fitMap(); + } + private _fitMap(): void { if (!this._leafletMap || !this.Leaflet || !this._config || !this.hass) { return; From af0304bf785640712f3b7d0eb47249285849ad40 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 3 Sep 2019 06:18:47 +0200 Subject: [PATCH 05/43] Add haptic feedback to handle click (#3569) --- src/panels/lovelace/common/handle-click.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/panels/lovelace/common/handle-click.ts b/src/panels/lovelace/common/handle-click.ts index 5ee86f965f..001ceb6d50 100644 --- a/src/panels/lovelace/common/handle-click.ts +++ b/src/panels/lovelace/common/handle-click.ts @@ -3,6 +3,7 @@ import { fireEvent } from "../../../common/dom/fire_event"; import { navigate } from "../../../common/navigate"; import { toggleEntity } from "../../../../src/panels/lovelace/common/entity/toggle-entity"; import { ActionConfig } from "../../../data/lovelace"; +import { forwardHaptic } from "../../../data/haptics"; export const handleClick = ( node: HTMLElement, @@ -49,10 +50,12 @@ export const handleClick = ( break; case "call-service": { if (!actionConfig.service) { + forwardHaptic("failure"); return; } const [domain, service] = actionConfig.service.split(".", 2); hass.callService(domain, service, actionConfig.service_data); } } + forwardHaptic("light"); }; From fe2046c6cd7fd3f87e39d9e3cb6b736396f25620 Mon Sep 17 00:00:00 2001 From: Ian Richardson Date: Tue, 3 Sep 2019 03:02:34 -0500 Subject: [PATCH 06/43] Filter camera service entities (#3583) Closes https://github.com/home-assistant/home-assistant-polymer/issues/3582 --- src/data/entity.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/entity.ts b/src/data/entity.ts index 506ff75edd..320924c7dd 100644 --- a/src/data/entity.ts +++ b/src/data/entity.ts @@ -6,6 +6,7 @@ export const ENTITY_COMPONENT_DOMAINS = [ "automation", "binary_sensor", "calendar", + "camera", "counter", "cover", "dominos", From 0e82178973e74c72485804c8e11f9328db846057 Mon Sep 17 00:00:00 2001 From: Yosi Levy <37745463+yosilevy@users.noreply.github.com> Date: Tue, 3 Sep 2019 12:31:08 +0300 Subject: [PATCH 07/43] Notification drawer RTL support (#3580) --- src/dialogs/notifications/notification-drawer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialogs/notifications/notification-drawer.js b/src/dialogs/notifications/notification-drawer.js index 614c752487..9b0698ea9b 100644 --- a/src/dialogs/notifications/notification-drawer.js +++ b/src/dialogs/notifications/notification-drawer.js @@ -49,7 +49,7 @@ export class HuiNotificationDrawer extends EventsMixin( text-align: center; } - +
[[localize('ui.notification_drawer.title')]]
From 4e383e3e67b6871454bfdf0cdd8d804a12f2abe5 Mon Sep 17 00:00:00 2001 From: MatthewFlamm <39341281+MatthewFlamm@users.noreply.github.com> Date: Tue, 3 Sep 2019 05:31:54 -0400 Subject: [PATCH 08/43] add exceptional icon (#3572) --- src/cards/ha-weather-card.js | 1 + src/dialogs/more-info/controls/more-info-weather.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/cards/ha-weather-card.js b/src/cards/ha-weather-card.js index 3a86d465e1..52caa5afae 100644 --- a/src/cards/ha-weather-card.js +++ b/src/cards/ha-weather-card.js @@ -281,6 +281,7 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) { this.weatherIcons = { "clear-night": "hass:weather-night", cloudy: "hass:weather-cloudy", + exceptional: "hass:alert-circle-outline", fog: "hass:weather-fog", hail: "hass:weather-hail", lightning: "hass:weather-lightning", diff --git a/src/dialogs/more-info/controls/more-info-weather.js b/src/dialogs/more-info/controls/more-info-weather.js index bbf7f97f11..a9309905cd 100644 --- a/src/dialogs/more-info/controls/more-info-weather.js +++ b/src/dialogs/more-info/controls/more-info-weather.js @@ -158,6 +158,7 @@ class MoreInfoWeather extends LocalizeMixin(PolymerElement) { this.weatherIcons = { "clear-night": "hass:weather-night", cloudy: "hass:weather-cloudy", + exceptional: "hass:alert-circle-outline", fog: "hass:weather-fog", hail: "hass:weather-hail", lightning: "hass:weather-lightning", From 87b35010e01f1426f201a81ac1ed1d7d37f9cbaf Mon Sep 17 00:00:00 2001 From: Ian Richardson Date: Tue, 3 Sep 2019 04:33:07 -0500 Subject: [PATCH 09/43] Add options to badges (#3552) * Add options to badges name icon entity_picture * lint * lint * rename entityPicture to image --- src/components/entity/ha-state-label-badge.ts | 16 +++++++++++++--- src/panels/lovelace/entity-rows/types.ts | 1 + src/panels/lovelace/hui-view.ts | 8 +++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/components/entity/ha-state-label-badge.ts b/src/components/entity/ha-state-label-badge.ts index 8f87caca18..0361adfcfd 100644 --- a/src/components/entity/ha-state-label-badge.ts +++ b/src/components/entity/ha-state-label-badge.ts @@ -29,6 +29,12 @@ export class HaStateLabelBadge extends LitElement { @property() public state?: HassEntity; + @property() public name?: string; + + @property() public icon?: string; + + @property() public image?: string; + @property() private _timerTimeRemaining?: number; private _connected?: boolean; @@ -72,10 +78,14 @@ export class HaStateLabelBadge extends LitElement { "has-unit_of_measurement": "unit_of_measurement" in state.attributes, })}" .value="${this._computeValue(domain, state)}" - .icon="${this._computeIcon(domain, state)}" - .image="${state.attributes.entity_picture}" + .icon="${this.icon ? this.icon : this._computeIcon(domain, state)}" + .image="${this.icon + ? "" + : this.image + ? this.image + : state.attributes.entity_picture}" .label="${this._computeLabel(domain, state, this._timerTimeRemaining)}" - .description="${computeStateName(state)}" + .description="${this.name ? this.name : computeStateName(state)}" > `; } diff --git a/src/panels/lovelace/entity-rows/types.ts b/src/panels/lovelace/entity-rows/types.ts index 19ba7b134f..c827e6b349 100644 --- a/src/panels/lovelace/entity-rows/types.ts +++ b/src/panels/lovelace/entity-rows/types.ts @@ -5,6 +5,7 @@ export interface EntityConfig { type?: string; name?: string; icon?: string; + image?: string; } export interface DividerConfig { type: "divider"; diff --git a/src/panels/lovelace/hui-view.ts b/src/panels/lovelace/hui-view.ts index ffba5b5331..06cd4b04fc 100644 --- a/src/panels/lovelace/hui-view.ts +++ b/src/panels/lovelace/hui-view.ts @@ -24,6 +24,7 @@ import { showEditCardDialog } from "./editor/card-editor/show-edit-card-dialog"; import { HuiErrorCard } from "./cards/hui-error-card"; import { computeRTL } from "../../common/util/compute_rtl"; +import { processConfigEntities } from "./common/process-config-entities"; let editCodeLoaded = false; @@ -262,10 +263,15 @@ export class HUIView extends LitElement { } const elements: HUIView["_badges"] = []; - for (const entityId of config.badges) { + const badges = processConfigEntities(config.badges); + for (const badge of badges) { const element = document.createElement("ha-state-label-badge"); + const entityId = badge.entity; element.hass = this.hass; element.state = this.hass!.states[entityId]; + element.name = badge.name; + element.icon = badge.icon; + element.image = badge.image; elements.push({ element, entityId }); root.appendChild(element); } From b37a0e2d43fbad83e905e112d382aa80545ee888 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 3 Sep 2019 12:56:11 +0200 Subject: [PATCH 10/43] Align styling cast buttons (#3579) * Align styling cast buttons * Split dev constants * Ignore dev_const * Update README.md --- .gitignore | 3 +++ cast/README.md | 2 +- src/cast/cast_manager.ts | 3 ++- src/cast/const.ts | 10 +++------- src/cast/dev_const.ts | 7 +++++++ src/cast/receiver_messages.ts | 3 ++- src/panels/lovelace/special-rows/hui-cast-row.ts | 15 +++++++++++---- 7 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 src/cast/dev_const.ts diff --git a/.gitignore b/.gitignore index 743889ba4e..8b568f884c 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,9 @@ dist .vscode/* !.vscode/extensions.json +# Cast dev settings +src/cast/dev_const.ts + # Secrets .lokalise_token yarn-error.log diff --git a/cast/README.md b/cast/README.md index aaf3141759..c1741fc989 100644 --- a/cast/README.md +++ b/cast/README.md @@ -25,7 +25,7 @@ Home Assistant Cast is made up of two separate applications: ### Setting dev variables -Open `src/cast/const.ts` and change `CAST_DEV` to `true` and `CAST_DEV_APP_ID` to the ID of the app you just created. +Open `src/cast/dev_const.ts` and change `CAST_DEV_APP_ID` to the ID of the app you just created. And set the `CAST_DEV_HASS_URL` to the url of you development machine. ### Changing configuration diff --git a/src/cast/cast_manager.ts b/src/cast/cast_manager.ts index 87404d3841..6c9ea0a8e1 100644 --- a/src/cast/cast_manager.ts +++ b/src/cast/cast_manager.ts @@ -1,5 +1,6 @@ import { castApiAvailable } from "./cast_framework"; -import { CAST_APP_ID, CAST_NS, CAST_DEV_HASS_URL, CAST_DEV } from "./const"; +import { CAST_APP_ID, CAST_NS, CAST_DEV } from "./const"; +import { CAST_DEV_HASS_URL } from "./dev_const"; import { castSendAuth, HassMessage as ReceiverMessage, diff --git a/src/cast/const.ts b/src/cast/const.ts index 784c159a72..fe578e8ad1 100644 --- a/src/cast/const.ts +++ b/src/cast/const.ts @@ -1,11 +1,7 @@ +import { CAST_DEV_APP_ID } from "./dev_const"; + // Guard dev mode with `__dev__` so it can only ever be enabled in dev mode. export const CAST_DEV = __DEV__ && true; -// Replace this with your own unpublished cast app that points at your local dev -const CAST_DEV_APP_ID = "5FE44367"; + export const CAST_APP_ID = CAST_DEV ? CAST_DEV_APP_ID : "B12CE3CA"; export const CAST_NS = "urn:x-cast:com.nabucasa.hast"; - -// Chromecast SDK will only load on localhost and HTTPS -// So during local development we have to send our dev IP address, -// but then run the UI on localhost. -export const CAST_DEV_HASS_URL = "http://192.168.1.234:8123"; diff --git a/src/cast/dev_const.ts b/src/cast/dev_const.ts new file mode 100644 index 0000000000..0e87495ce8 --- /dev/null +++ b/src/cast/dev_const.ts @@ -0,0 +1,7 @@ +// Replace this with your own unpublished cast app that points at your local dev +export const CAST_DEV_APP_ID = "5FE44367"; + +// Chromecast SDK will only load on localhost and HTTPS +// So during local development we have to send our dev IP address, +// but then run the UI on localhost. +export const CAST_DEV_HASS_URL = "http://192.168.1.234:8123"; diff --git a/src/cast/receiver_messages.ts b/src/cast/receiver_messages.ts index 529d5e4e70..b3d96c712b 100644 --- a/src/cast/receiver_messages.ts +++ b/src/cast/receiver_messages.ts @@ -4,7 +4,8 @@ import { Auth } from "home-assistant-js-websocket"; import { CastManager } from "./cast_manager"; import { BaseCastMessage } from "./types"; -import { CAST_DEV_HASS_URL, CAST_DEV } from "./const"; +import { CAST_DEV } from "./const"; +import { CAST_DEV_HASS_URL } from "./dev_const"; export interface GetStatusMessage extends BaseCastMessage { type: "get_status"; diff --git a/src/panels/lovelace/special-rows/hui-cast-row.ts b/src/panels/lovelace/special-rows/hui-cast-row.ts index a71ae295fc..9b5bc7eda2 100644 --- a/src/panels/lovelace/special-rows/hui-cast-row.ts +++ b/src/panels/lovelace/special-rows/hui-cast-row.ts @@ -7,6 +7,7 @@ import { css, CSSResult, } from "lit-element"; +import { classMap } from "lit-html/directives/class-map"; import { EntityRow, CastConfig } from "../entity-rows/types"; import { HomeAssistant } from "../../../types"; @@ -45,6 +46,11 @@ class HuiCastRow extends LitElement implements EntityRow { return html``; } + const active = + this._castManager && + this._castManager.status && + this._config.view === this._castManager.status.lovelacePath; + return html`
@@ -68,8 +74,8 @@ class HuiCastRow extends LitElement implements EntityRow { SHOW @@ -124,7 +130,6 @@ class HuiCastRow extends LitElement implements EntityRow { :host { display: flex; align-items: center; - overflow: visible; } ha-icon { padding: 8px; @@ -143,7 +148,6 @@ class HuiCastRow extends LitElement implements EntityRow { text-overflow: ellipsis; } .controls { - margin-right: -0.57em; display: flex; align-items: center; } @@ -154,6 +158,9 @@ class HuiCastRow extends LitElement implements EntityRow { height: 24px; width: 24px; } + .inactive { + padding: 0 4px; + } `; } } From 753e069323491b176cb3b4c9562f7fe17e12ac9e Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 3 Sep 2019 13:13:29 +0200 Subject: [PATCH 11/43] Move lovelace background settings to theme (#3561) * Move lovelace background settings to theme While being backwards compatible * Also update cast --- cast/src/receiver/layout/hc-lovelace.ts | 12 +++++++++--- src/panels/lovelace/hui-root.ts | 16 +++++++++++++--- src/panels/lovelace/hui-unused-entities.ts | 3 +++ src/panels/lovelace/hui-view.ts | 1 + 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/cast/src/receiver/layout/hc-lovelace.ts b/cast/src/receiver/layout/hc-lovelace.ts index 0be9488a54..3c52475fd6 100644 --- a/cast/src/receiver/layout/hc-lovelace.ts +++ b/cast/src/receiver/layout/hc-lovelace.ts @@ -57,10 +57,16 @@ class HcLovelace extends LitElement { const index = this._viewIndex; if (index !== undefined) { - this.shadowRoot!.querySelector("hui-view")!.style.background = + const configBackground = this.lovelaceConfig.views[index].background || - this.lovelaceConfig.background || - ""; + this.lovelaceConfig.background; + + if (configBackground) { + this.shadowRoot!.querySelector("hui-view")!.style.setProperty( + "--lovelace-background", + configBackground + ); + } } } } diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts index ce513e27bb..7780070262 100644 --- a/src/panels/lovelace/hui-root.ts +++ b/src/panels/lovelace/hui-root.ts @@ -568,7 +568,12 @@ class HUIRoot extends LitElement { unusedEntities.hass = this.hass!; } ); - root.style.background = this.config.background || ""; + if (this.config.background) { + unusedEntities.style.setProperty( + "--lovelace-background", + this.config.background + ); + } root.append(unusedEntities); return; } @@ -597,8 +602,13 @@ class HUIRoot extends LitElement { } view.hass = this.hass; - root.style.background = - viewConfig.background || this.config.background || ""; + + const configBackground = viewConfig.background || this.config.background; + + if (configBackground) { + view.style.setProperty("--lovelace-background", configBackground); + } + root.append(view); } } diff --git a/src/panels/lovelace/hui-unused-entities.ts b/src/panels/lovelace/hui-unused-entities.ts index 949dd0f38a..55ab49cf1e 100644 --- a/src/panels/lovelace/hui-unused-entities.ts +++ b/src/panels/lovelace/hui-unused-entities.ts @@ -56,6 +56,9 @@ export class HuiUnusedEntities extends LitElement { private renderStyle(): TemplateResult { return html` - - - - - - -
- -
- - - -
-
- Publish -
-
-
- `; - } - - static get properties() { - return { - hass: Object, - topic: String, - payload: String, - }; - } - - _publish() { - this.hass.callService("mqtt", "publish", { - topic: this.topic, - payload_template: this.payload, - }); - } -} - -customElements.define("developer-tools-mqtt", HaPanelDevMqtt); diff --git a/src/panels/developer-tools/mqtt/developer-tools-mqtt.ts b/src/panels/developer-tools/mqtt/developer-tools-mqtt.ts new file mode 100644 index 0000000000..3320eb52c3 --- /dev/null +++ b/src/panels/developer-tools/mqtt/developer-tools-mqtt.ts @@ -0,0 +1,126 @@ +import { + LitElement, + customElement, + TemplateResult, + html, + property, + CSSResultArray, + css, +} from "lit-element"; +import "@material/mwc-button"; +import "@polymer/paper-input/paper-input"; +import "@polymer/paper-input/paper-textarea"; + +import { HomeAssistant } from "../../../types"; + +import { haStyle } from "../../../resources/styles"; +import "../../../components/ha-card"; +import "./mqtt-subscribe-card"; + +@customElement("developer-tools-mqtt") +class HaPanelDevMqtt extends LitElement { + @property() public hass?: HomeAssistant; + + @property() private topic = ""; + + @property() private payload = ""; + + private inited: boolean = false; + + protected firstUpdated() { + if (localStorage && localStorage["panel-dev-mqtt-topic"]) { + this.topic = localStorage["panel-dev-mqtt-topic"]; + } + if (localStorage && localStorage["panel-dev-mqtt-payload"]) { + this.payload = localStorage["panel-dev-mqtt-payload"]; + } + this.inited = true; + } + + protected render(): TemplateResult { + return html` +
+ +
+ + + +
+
+ Publish +
+
+ + +
+ `; + } + + private _handleTopic(ev: CustomEvent) { + this.topic = ev.detail.value; + if (localStorage && this.inited) { + localStorage["panel-dev-mqtt-topic"] = this.topic; + } + } + + private _handlePayload(ev: CustomEvent) { + this.payload = ev.detail.value; + if (localStorage && this.inited) { + localStorage["panel-dev-mqtt-payload"] = this.payload; + } + } + + private _publish(): void { + if (!this.hass) { + return; + } + this.hass.callService("mqtt", "publish", { + topic: this.topic, + payload_template: this.payload, + }); + } + + static get styles(): CSSResultArray { + return [ + haStyle, + css` + :host { + -ms-user-select: initial; + -webkit-user-select: initial; + -moz-user-select: initial; + } + + .content { + padding: 24px 0 32px; + max-width: 600px; + margin: 0 auto; + direction: ltr; + } + + mwc-button { + background-color: white; + } + + mqtt-subscribe-card { + display: block; + margin: 16px auto; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "developer-tools-mqtt": HaPanelDevMqtt; + } +} diff --git a/src/panels/developer-tools/mqtt/mqtt-subscribe-card.ts b/src/panels/developer-tools/mqtt/mqtt-subscribe-card.ts new file mode 100644 index 0000000000..0795fcad30 --- /dev/null +++ b/src/panels/developer-tools/mqtt/mqtt-subscribe-card.ts @@ -0,0 +1,153 @@ +import { + LitElement, + customElement, + TemplateResult, + html, + property, + CSSResult, + css, +} from "lit-element"; +import "@material/mwc-button"; +import "@polymer/paper-input/paper-input"; +import { HomeAssistant } from "../../../types"; +import "../../../components/ha-card"; +import format_time from "../../../common/datetime/format_time"; + +import { subscribeMQTTTopic, MQTTMessage } from "../../../data/mqtt"; + +@customElement("mqtt-subscribe-card") +class MqttSubscribeCard extends LitElement { + @property() public hass?: HomeAssistant; + + @property() private _topic = ""; + + @property() private _subscribed?: () => void; + + @property() private _messages: Array<{ + id: number; + message: MQTTMessage; + payload: string; + time: Date; + }> = []; + + private _messageCount = 0; + + public disconnectedCallback() { + super.disconnectedCallback(); + if (this._subscribed) { + this._subscribed(); + this._subscribed = undefined; + } + } + + protected render(): TemplateResult { + return html` + +
+ + + ${this._subscribed ? "Stop listening" : "Start listening"} + +
+
+ ${this._messages.map( + (msg) => html` +
+ Message ${msg.id} received on ${msg.message.topic} at + ${format_time(msg.time, this.hass!.language)}: +
${msg.payload}
+
+ QoS: ${msg.message.qos} - Retain: + ${Boolean(msg.message.retain)} +
+
+ ` + )} +
+
+ `; + } + + private _valueChanged(ev: CustomEvent): void { + this._topic = ev.detail.value; + } + + private async _handleSubmit(): Promise { + if (this._subscribed) { + this._subscribed(); + this._subscribed = undefined; + } else { + this._subscribed = await subscribeMQTTTopic( + this.hass!, + this._topic, + (message) => this._handleMessage(message) + ); + } + } + + private _handleMessage(message: MQTTMessage) { + const tail = + this._messages.length > 30 ? this._messages.slice(0, 29) : this._messages; + let payload: string; + try { + payload = JSON.stringify(JSON.parse(message.payload), null, 4); + } catch (e) { + payload = message.payload; + } + this._messages = [ + { + payload, + message, + time: new Date(), + id: this._messageCount++, + }, + ...tail, + ]; + } + + static get styles(): CSSResult { + return css` + form { + display: block; + padding: 16px; + } + paper-input { + display: inline-block; + width: 200px; + } + .events { + margin: -16px 0; + padding: 0 16px; + } + .event { + border-bottom: 1px solid var(--divider-color); + padding-bottom: 16px; + margin: 16px 0; + } + .event:last-child { + border-bottom: 0; + } + .bottom { + font-size: 80%; + color: var(--secondary-text-color); + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "mqtt-subscribe-card": MqttSubscribeCard; + } +} From 48a010563ee839daf14ef524f502d11091622df4 Mon Sep 17 00:00:00 2001 From: Ian Richardson Date: Wed, 4 Sep 2019 02:37:21 -0500 Subject: [PATCH 22/43] Wrap long attributes in more-info-default (#3601) Can likely be applied in many other places Closes https://github.com/home-assistant/home-assistant-polymer/issues/2811 --- src/components/ha-attributes.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ha-attributes.js b/src/components/ha-attributes.js index ae57a454b0..1a1836c2f2 100644 --- a/src/components/ha-attributes.js +++ b/src/components/ha-attributes.js @@ -11,6 +11,7 @@ class HaAttributes extends PolymerElement {