From 2ae137bbc2c22dc1794ea90a512579286e4dd65d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Mon, 12 Dec 2022 16:22:34 +0100 Subject: [PATCH 01/14] Add fallback URLs (#14720) --- .github/workflows/demo_deployment.yaml | 2 +- .github/workflows/design_deployment.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/demo_deployment.yaml b/.github/workflows/demo_deployment.yaml index aa489d0ba6..24338b948c 100644 --- a/.github/workflows/demo_deployment.yaml +++ b/.github/workflows/demo_deployment.yaml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest environment: name: Demo - url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL }} + url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} steps: - name: Check out files from GitHub uses: actions/checkout@v3 diff --git a/.github/workflows/design_deployment.yaml b/.github/workflows/design_deployment.yaml index e152f7b4c1..0743c1d800 100644 --- a/.github/workflows/design_deployment.yaml +++ b/.github/workflows/design_deployment.yaml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest environment: name: Design - url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL }} + url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} steps: - name: Check out files from GitHub uses: actions/checkout@v3 From f83544dd38286de611081d572c3f7075c4396a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Mon, 12 Dec 2022 16:23:11 +0100 Subject: [PATCH 02/14] Add action for design preview (#14721) * Add action for design preview * Remove netlify_build_gallery --- .github/workflows/design_preview.yaml | 52 +++++++++++++++++++++++++++ gallery/script/netlify_build_gallery | 34 ------------------ 2 files changed, 52 insertions(+), 34 deletions(-) create mode 100644 .github/workflows/design_preview.yaml delete mode 100755 gallery/script/netlify_build_gallery diff --git a/.github/workflows/design_preview.yaml b/.github/workflows/design_preview.yaml new file mode 100644 index 0000000000..a209613620 --- /dev/null +++ b/.github/workflows/design_preview.yaml @@ -0,0 +1,52 @@ +name: Design preview + +on: + pull_request: + types: + - opened + - synchronize + - reopened + - labeled + branches: + - dev + +env: + NODE_VERSION: 16 + NODE_OPTIONS: --max_old_space_size=6144 + +jobs: + preview: + runs-on: ubuntu-latest + if: contains(github.event.pull_request.labels.*.name, 'needs design preview') + steps: + - name: Check out files from GitHub + uses: actions/checkout@v3 + + - name: Set up Node ${{ env.NODE_VERSION }} + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + + - name: Install dependencies + run: yarn install + env: + CI: true + + - name: Build Gallery + run: ./node_modules/.bin/gulp build-gallery + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Deploy preview to Netlify + id: deploy + uses: netlify/actions/cli@master + with: + args: deploy --dir=gallery/dist --alias "deploy-preview-${{ github.event.number }}" + env: + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_GALLERY_SITE_ID }} + + - name: Generate summary + run: | + echo "${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}" >> "$GITHUB_STEP_SUMMARY" diff --git a/gallery/script/netlify_build_gallery b/gallery/script/netlify_build_gallery deleted file mode 100755 index 97c95ebf2a..0000000000 --- a/gallery/script/netlify_build_gallery +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -TARGET_LABEL="needs design preview" -export SKIP_FETCH_NIGHTLY_TRANSLATIONS="true" - -if [[ "$NETLIFY" != "true" ]]; then - echo "This script can only be run on Netlify" - exit 1 -fi - -function createStatus() { - state="$1" - description="$2" - target_url="$3" - curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: token $GITHUB_TOKEN" \ - "https://api.github.com/repos/home-assistant/frontend/statuses/$COMMIT_REF" \ - -d '{"state": "'"${state}"'", "context": "Netlify/Design Preview Build", "description": "'"$description"'", "target_url": "'"$target_url"'"}' -} - - -if [[ "${PULL_REQUEST}" == "true" ]]; then - if [[ "$(curl -sSLf -H "Accept: application/vnd.github.v3+json" -H "Authorization: token $GITHUB_TOKEN" \ - "https://api.github.com/repos/home-assistant/frontend/pulls/${REVIEW_ID}" | jq '.labels[].name' -r)" =~ "$TARGET_LABEL" ]]; then - createStatus "pending" "Building design preview" "https://app.netlify.com/sites/home-assistant-gallery/deploys/$BUILD_ID" - gulp build-gallery - if [ $? -eq 0 ]; then - createStatus "success" "Build complete" "$DEPLOY_PRIME_URL" - else - createStatus "error" "Build failed" "https://app.netlify.com/sites/home-assistant-gallery/deploys/$BUILD_ID" - fi - else - createStatus "success" "Build was not requested by PR label" - fi -fi From 8d1ecdb27e087aa55cb21f06024db48a6c0c1f59 Mon Sep 17 00:00:00 2001 From: Steve Repsher Date: Mon, 12 Dec 2022 11:45:06 -0500 Subject: [PATCH 03/14] Bump prettier to 2.8.1 (#14694) * Bump prettier to 2.8.1 * Reformat ha-data-table --- package.json | 5 +-- src/components/data-table/ha-data-table.ts | 12 +++-- yarn.lock | 52 +++++----------------- 3 files changed, 21 insertions(+), 48 deletions(-) diff --git a/package.json b/package.json index e6deb0a523..8577ac673e 100644 --- a/package.json +++ b/package.json @@ -193,12 +193,11 @@ "eslint": "^7.32.0", "eslint-config-airbnb-base": "^14.2.1", "eslint-config-airbnb-typescript": "^14.0.0", - "eslint-config-prettier": "^8.3.0", + "eslint-config-prettier": "^8.5.0", "eslint-import-resolver-webpack": "^0.13.1", "eslint-plugin-disable": "^2.0.1", "eslint-plugin-import": "^2.24.2", "eslint-plugin-lit": "^1.6.1", - "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-unused-imports": "^1.1.5", "eslint-plugin-wc": "^1.3.2", "fancy-log": "^1.3.3", @@ -224,7 +223,7 @@ "object-hash": "^2.0.3", "open": "^7.0.4", "pinst": "^3.0.0", - "prettier": "^2.4.1", + "prettier": "^2.8.1", "require-dir": "^1.2.0", "rollup": "^2.8.2", "rollup-plugin-string": "^3.0.0", diff --git a/src/components/data-table/ha-data-table.ts b/src/components/data-table/ha-data-table.ts index a28005bfba..a9d9bf3ea0 100644 --- a/src/components/data-table/ha-data-table.ts +++ b/src/components/data-table/ha-data-table.ts @@ -627,7 +627,9 @@ export class HaDataTable extends LitElement { border-top: 1px solid var(--divider-color); } - .mdc-data-table__row.clickable:not(.mdc-data-table__row--selected):hover { + .mdc-data-table__row.clickable:not( + .mdc-data-table__row--selected + ):hover { background-color: rgba(var(--rgb-primary-text-color), 0.04); } @@ -734,13 +736,17 @@ export class HaDataTable extends LitElement { } .mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:hover, - .mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:not(.not-sorted) { + .mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:not( + .not-sorted + ) { text-align: left; } :host([dir="rtl"]) .mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:hover, :host([dir="rtl"]) - .mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:not(.not-sorted) { + .mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:not( + .not-sorted + ) { text-align: right; } diff --git a/yarn.lock b/yarn.lock index ae887067d5..49872cc55b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7662,14 +7662,14 @@ __metadata: languageName: node linkType: hard -"eslint-config-prettier@npm:^8.3.0": - version: 8.3.0 - resolution: "eslint-config-prettier@npm:8.3.0" +"eslint-config-prettier@npm:^8.5.0": + version: 8.5.0 + resolution: "eslint-config-prettier@npm:8.5.0" peerDependencies: eslint: ">=7.0.0" bin: eslint-config-prettier: bin/cli.js - checksum: df4cea3032671995bb5ab07e016169072f7fa59f44a53251664d9ca60951b66cdc872683b5c6a3729c91497c11490ca44a79654b395dd6756beb0c3903a37196 + checksum: 0d0f5c32e7a0ad91249467ce71ca92394ccd343178277d318baf32063b79ea90216f4c81d1065d60f96366fdc60f151d4d68ae7811a58bd37228b84c2083f893 languageName: node linkType: hard @@ -7765,21 +7765,6 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-prettier@npm:^4.0.0": - version: 4.0.0 - resolution: "eslint-plugin-prettier@npm:4.0.0" - dependencies: - prettier-linter-helpers: ^1.0.0 - peerDependencies: - eslint: ">=7.28.0" - prettier: ">=2.0.0" - peerDependenciesMeta: - eslint-config-prettier: - optional: true - checksum: 03d69177a3c21fa2229c7e427ce604429f0b20ab7f411e2e824912f572a207c7f5a41fd1f0a95b9b8afe121e291c1b1f1dc1d44c7aad4b0837487f9c19f5210d - languageName: node - linkType: hard - "eslint-plugin-unused-imports@npm:^1.1.5": version: 1.1.5 resolution: "eslint-plugin-unused-imports@npm:1.1.5" @@ -8228,13 +8213,6 @@ __metadata: languageName: node linkType: hard -"fast-diff@npm:^1.1.2": - version: 1.2.0 - resolution: "fast-diff@npm:1.2.0" - checksum: 1b5306eaa9e826564d9e5ffcd6ebd881eb5f770b3f977fcbf38f05c824e42172b53c79920e8429c54eb742ce15a0caf268b0fdd5b38f6de52234c4a8368131ae - languageName: node - linkType: hard - "fast-glob@npm:^2.2.6": version: 2.2.7 resolution: "fast-glob@npm:2.2.7" @@ -9401,12 +9379,11 @@ fsevents@^1.2.7: eslint: ^7.32.0 eslint-config-airbnb-base: ^14.2.1 eslint-config-airbnb-typescript: ^14.0.0 - eslint-config-prettier: ^8.3.0 + eslint-config-prettier: ^8.5.0 eslint-import-resolver-webpack: ^0.13.1 eslint-plugin-disable: ^2.0.1 eslint-plugin-import: ^2.24.2 eslint-plugin-lit: ^1.6.1 - eslint-plugin-prettier: ^4.0.0 eslint-plugin-unused-imports: ^1.1.5 eslint-plugin-wc: ^1.3.2 fancy-log: ^1.3.3 @@ -9446,7 +9423,7 @@ fsevents@^1.2.7: object-hash: ^2.0.3 open: ^7.0.4 pinst: ^3.0.0 - prettier: ^2.4.1 + prettier: ^2.8.1 proxy-polyfill: ^0.3.2 punycode: ^2.1.1 qr-scanner: ^1.3.0 @@ -13047,21 +13024,12 @@ fsevents@^1.2.7: languageName: node linkType: hard -"prettier-linter-helpers@npm:^1.0.0": - version: 1.0.0 - resolution: "prettier-linter-helpers@npm:1.0.0" - dependencies: - fast-diff: ^1.1.2 - checksum: 00ce8011cf6430158d27f9c92cfea0a7699405633f7f1d4a45f07e21bf78e99895911cbcdc3853db3a824201a7c745bd49bfea8abd5fb9883e765a90f74f8392 - languageName: node - linkType: hard - -"prettier@npm:^2.4.1": - version: 2.4.1 - resolution: "prettier@npm:2.4.1" +"prettier@npm:^2.8.1": + version: 2.8.1 + resolution: "prettier@npm:2.8.1" bin: prettier: bin-prettier.js - checksum: cc6830588b401b0d742862fe9c46bc9118204fb307c3abe0e49e95b35ed23629573807ffdf9cdd65289c252a0bb51fc0171437f6626ee36378dea80f0ee80b91 + checksum: 4f21a0f1269f76fb36f54e9a8a1ea4c11e27478958bf860661fb4b6d7ac69aac1581f8724fa98ea3585e56d42a2ea317a17ff6e3324f40cb11ff9e20b73785cc languageName: node linkType: hard From c1dba217daf5d461dbd2bc482c275dc501c12fe0 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 12 Dec 2022 20:20:56 +0100 Subject: [PATCH 04/14] Make error on general config better + default timezone (#14635) --- src/panels/config/core/ha-config-section-general.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/panels/config/core/ha-config-section-general.ts b/src/panels/config/core/ha-config-section-general.ts index f3857b1bb7..635c455d79 100644 --- a/src/panels/config/core/ha-config-section-general.ts +++ b/src/panels/config/core/ha-config-section-general.ts @@ -25,6 +25,7 @@ import { SYMBOL_TO_ISO } from "../../../data/currency"; import "../../../layouts/hass-subpage"; import { haStyle } from "../../../resources/styles"; import type { HomeAssistant } from "../../../types"; +import "../../../components/ha-alert"; @customElement("ha-config-section-general") class HaConfigSectionGeneral extends LitElement { @@ -52,6 +53,8 @@ class HaConfigSectionGeneral extends LitElement { @state() private _languages?: { value: string; label: string }[]; + @state() private _error?: string; + protected render(): TemplateResult { const canEdit = ["storage", "default"].includes( this.hass.config.config_source @@ -65,6 +68,9 @@ class HaConfigSectionGeneral extends LitElement { .header=${this.hass.localize("ui.panel.config.core.caption")} >
+ ${this._error + ? html`${this._error}` + : ""}
${!canEdit @@ -289,7 +295,7 @@ class HaConfigSectionGeneral extends LitElement { this._country = this.hass.config.country; this._language = this.hass.config.language; this._elevation = this.hass.config.elevation; - this._timeZone = this.hass.config.time_zone; + this._timeZone = this.hass.config.time_zone || "Etc/GMT"; this._name = this.hass.config.location_name; this._computeLanguages(); } @@ -347,6 +353,8 @@ class HaConfigSectionGeneral extends LitElement { }; } + this._error = undefined; + try { await saveCoreConfig(this.hass, { currency: this._currency, @@ -361,7 +369,7 @@ class HaConfigSectionGeneral extends LitElement { button.actionSuccess(); } catch (err: any) { button.actionError(); - alert(`Error saving config: ${err.message}`); + this._error = err.message; } finally { button.progress = false; } From 180357e0db535d03ad63ca0e8cd53d4f3b9921b4 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Tue, 13 Dec 2022 09:59:26 +0100 Subject: [PATCH 05/14] Align domain icon for person and device_tracker when away (#14652) --- src/common/entity/domain_icon.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/common/entity/domain_icon.ts b/src/common/entity/domain_icon.ts index 5da076f2da..6b8b88bde5 100644 --- a/src/common/entity/domain_icon.ts +++ b/src/common/entity/domain_icon.ts @@ -180,6 +180,9 @@ export const domainIconWithoutDefault = ( } } + case "person": + return compareState === "not_home" ? mdiAccountArrowRight : mdiAccount; + case "switch": switch (stateObj?.attributes.device_class) { case "outlet": From 5cabf1d041e176cbde8f998d4435fded19ae16c2 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 13 Dec 2022 11:07:57 +0100 Subject: [PATCH 06/14] Use compute state display for select (#14736) --- .../entity-rows/hui-select-entity-row.ts | 18 +++++++++--------- src/state-summary/state-card-select.ts | 14 ++++++++------ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/panels/lovelace/entity-rows/hui-select-entity-row.ts b/src/panels/lovelace/entity-rows/hui-select-entity-row.ts index a169cbe565..d11f473aa4 100644 --- a/src/panels/lovelace/entity-rows/hui-select-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-select-entity-row.ts @@ -9,6 +9,7 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import { stopPropagation } from "../../../common/dom/stop_propagation"; +import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { computeStateName } from "../../../common/entity/compute_state_name"; import "../../../components/ha-select"; import { UNAVAILABLE } from "../../../data/entity"; @@ -76,15 +77,14 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow { ? stateObj.attributes.options.map( (option) => html` - ${(stateObj.attributes.device_class && - this.hass!.localize( - `component.select.state.${stateObj.attributes.device_class}.${option}` - )) || - this.hass!.localize( - `component.select.state._.${option}` - ) || - option} + + ${computeStateDisplay( + this.hass!.localize, + stateObj, + this.hass!.locale, + this.hass!.entities, + option + )} ` ) diff --git a/src/state-summary/state-card-select.ts b/src/state-summary/state-card-select.ts index a37c8e07ad..78c749c48e 100644 --- a/src/state-summary/state-card-select.ts +++ b/src/state-summary/state-card-select.ts @@ -8,6 +8,7 @@ import "../components/entity/state-badge"; import { UNAVAILABLE } from "../data/entity"; import { SelectEntity, setSelectOption } from "../data/select"; import type { HomeAssistant } from "../types"; +import { computeStateDisplay } from "../common/entity/compute_state_display"; @customElement("state-card-select") class StateCardSelect extends LitElement { @@ -31,12 +32,13 @@ class StateCardSelect extends LitElement { (option) => html` - ${(this.stateObj.attributes.device_class && - this.hass.localize( - `component.select.state.${this.stateObj.attributes.device_class}.${option}` - )) || - this.hass.localize(`component.select.state._.${option}`) || - option} + ${computeStateDisplay( + this.hass.localize, + this.stateObj, + this.hass.locale, + this.hass.entities, + option + )} ` )} From d64bb98bb4cefc3cc2e57be7085304fcfd519292 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 13 Dec 2022 12:11:09 +0100 Subject: [PATCH 07/14] Fix binary sensor color when off (#14735) --- src/common/entity/color/battery_color.ts | 2 +- src/common/entity/color/binary_sensor_color.ts | 9 ++++++++- src/common/entity/color/update_color.ts | 15 +++++++++++++++ src/common/entity/state_color.ts | 8 +++----- src/resources/ha-style.ts | 1 - 5 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 src/common/entity/color/update_color.ts diff --git a/src/common/entity/color/battery_color.ts b/src/common/entity/color/battery_color.ts index daa3715450..b10c5144fe 100644 --- a/src/common/entity/color/battery_color.ts +++ b/src/common/entity/color/battery_color.ts @@ -1,7 +1,7 @@ export const batteryStateColor = (state: string) => { const value = Number(state); if (isNaN(value)) { - return "sensor-battery-unknown"; + return undefined; } if (value >= 70) { return "sensor-battery-high"; diff --git a/src/common/entity/color/binary_sensor_color.ts b/src/common/entity/color/binary_sensor_color.ts index 0d7949b39a..f3458214fd 100644 --- a/src/common/entity/color/binary_sensor_color.ts +++ b/src/common/entity/color/binary_sensor_color.ts @@ -1,4 +1,5 @@ import { HassEntity } from "home-assistant-js-websocket"; +import { stateActive } from "../state_active"; const ALERTING_DEVICE_CLASSES = new Set([ "battery", @@ -12,9 +13,15 @@ const ALERTING_DEVICE_CLASSES = new Set([ "tamper", ]); -export const binarySensorColor = (stateObj: HassEntity): string | undefined => { +export const binarySensorColor = ( + stateObj: HassEntity, + state: string +): string | undefined => { const deviceClass = stateObj?.attributes.device_class; + if (!stateActive(stateObj, state)) { + return undefined; + } return deviceClass && ALERTING_DEVICE_CLASSES.has(deviceClass) ? "binary-sensor-alerting" : "binary-sensor"; diff --git a/src/common/entity/color/update_color.ts b/src/common/entity/color/update_color.ts new file mode 100644 index 0000000000..b034239e95 --- /dev/null +++ b/src/common/entity/color/update_color.ts @@ -0,0 +1,15 @@ +import { HassEntity } from "home-assistant-js-websocket"; +import { UpdateEntity, updateIsInstalling } from "../../../data/update"; +import { stateActive } from "../state_active"; + +export const updateColor = ( + stateObj: HassEntity, + state: string +): string | undefined => { + if (!stateActive(stateObj, state)) { + return undefined; + } + return updateIsInstalling(stateObj as UpdateEntity) + ? "update-installing" + : "update"; +}; diff --git a/src/common/entity/state_color.ts b/src/common/entity/state_color.ts index aed20531e3..613ce4e7d8 100644 --- a/src/common/entity/state_color.ts +++ b/src/common/entity/state_color.ts @@ -1,13 +1,13 @@ /** Return an color representing a state. */ import { HassEntity } from "home-assistant-js-websocket"; import { UNAVAILABLE } from "../../data/entity"; -import { UpdateEntity, updateIsInstalling } from "../../data/update"; import { alarmControlPanelColor } from "./color/alarm_control_panel_color"; import { binarySensorColor } from "./color/binary_sensor_color"; import { climateColor } from "./color/climate_color"; import { lockColor } from "./color/lock_color"; import { personColor } from "./color/person_color"; import { sensorColor } from "./color/sensor_color"; +import { updateColor } from "./color/update_color"; import { computeDomain } from "./compute_domain"; import { stateActive } from "./state_active"; @@ -66,7 +66,7 @@ export const stateColor = (stateObj: HassEntity, state?: string) => { return alarmControlPanelColor(compareState); case "binary_sensor": - return binarySensorColor(stateObj); + return binarySensorColor(stateObj, compareState); case "climate": return climateColor(compareState); @@ -85,9 +85,7 @@ export const stateColor = (stateObj: HassEntity, state?: string) => { return compareState === "above_horizon" ? "sun-day" : "sun-night"; case "update": - return updateIsInstalling(stateObj as UpdateEntity) - ? "update-installing" - : "update"; + return updateColor(stateObj, compareState); } return undefined; diff --git a/src/resources/ha-style.ts b/src/resources/ha-style.ts index 07b2fbde32..ab7b577356 100644 --- a/src/resources/ha-style.ts +++ b/src/resources/ha-style.ts @@ -176,7 +176,6 @@ documentContainer.innerHTML = ` --rgb-state-sensor-battery-high-color: var(--rgb-green-color); --rgb-state-sensor-battery-low-color: var(--rgb-red-color); --rgb-state-sensor-battery-medium-color: var(--rgb-orange-color); - --rgb-state-sensor-battery-unknown-color: var(--rgb-off-color); --rgb-state-siren-color: var(--rgb-red-color); --rgb-state-sun-day-color: var(--rgb-amber-color); --rgb-state-sun-night-color: var(--rgb-deep-purple-color); From 2d7973af79a7cf5bb73231740b1f750d33bdf010 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 13 Dec 2022 14:02:48 +0100 Subject: [PATCH 08/14] Fix brightness on button card (#14724) --- src/panels/lovelace/cards/hui-button-card.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts index 4686c82f50..be8d189472 100644 --- a/src/panels/lovelace/cards/hui-button-card.ts +++ b/src/panels/lovelace/cards/hui-button-card.ts @@ -309,7 +309,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { } private _computeBrightness(stateObj: HassEntity | LightEntity): string { - if (!stateObj.attributes.brightness && stateActive(stateObj)) { + if (stateObj.attributes.brightness) { const brightness = stateObj.attributes.brightness; return `brightness(${(brightness + 245) / 5}%)`; } @@ -319,7 +319,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { private _computeColor( stateObj: HassEntity | LightEntity ): string | undefined { - if (stateObj.attributes.rgb_color && stateActive(stateObj)) { + if (stateObj.attributes.rgb_color) { return `rgb(${stateObj.attributes.rgb_color.join(",")})`; } const iconColor = stateColorCss(stateObj); From 62bc171b8caacec9a015340a8850294160f67420 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 13 Dec 2022 14:17:13 +0100 Subject: [PATCH 09/14] Only use custom color when active on tile card (#14744) --- src/panels/lovelace/cards/hui-tile-card.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/panels/lovelace/cards/hui-tile-card.ts b/src/panels/lovelace/cards/hui-tile-card.ts index 17aea90e51..4ac603bbfe 100644 --- a/src/panels/lovelace/cards/hui-tile-card.ts +++ b/src/panels/lovelace/cards/hui-tile-card.ts @@ -9,6 +9,7 @@ import { hsv2rgb, rgb2hsv } from "../../../common/color/convert-color"; import { DOMAINS_TOGGLE } from "../../../common/const"; import { computeDomain } from "../../../common/entity/compute_domain"; import { computeStateDisplay } from "../../../common/entity/compute_state_display"; +import { stateActive } from "../../../common/entity/state_active"; import { stateColorCss } from "../../../common/entity/state_color"; import { stateIconPath } from "../../../common/entity/state_icon_path"; import { blankBeforePercent } from "../../../common/translations/blank_before_percent"; @@ -128,9 +129,9 @@ export class HuiTileCard extends LitElement implements LovelaceCard { } private _computeStateColor = memoize((entity: HassEntity, color?: string) => { - // Use custom color + // Use custom color if active if (color) { - return computeRgbColor(color); + return stateActive(entity) ? computeRgbColor(color) : undefined; } // Use default color for person/device_tracker because color is on the badge @@ -363,7 +364,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard { static get styles(): CSSResultGroup { return css` :host { - --tile-color: var(--rgb-state-default-color); + --tile-color: var(--rgb-state-inactive-color); --tile-tap-padding: 6px; -webkit-tap-highlight-color: transparent; } From b82d6fd35f7fe2ce2ac579e0ae918264a9dde8c1 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 13 Dec 2022 16:49:12 +0100 Subject: [PATCH 10/14] Fix climate hvac action color (#14747) --- gallery/src/pages/misc/entity-state.ts | 11 +++++++++++ src/common/entity/color/climate_color.ts | 10 ++++++++++ src/components/entity/state-badge.ts | 11 ++++++++++- src/panels/lovelace/cards/hui-button-card.ts | 12 +++++++++--- src/panels/lovelace/cards/hui-entity-card.ts | 13 +++++++++---- .../cards/tile/badges/tile-badge-climate.ts | 9 +-------- src/resources/ha-style.ts | 2 +- 7 files changed, 51 insertions(+), 17 deletions(-) diff --git a/gallery/src/pages/misc/entity-state.ts b/gallery/src/pages/misc/entity-state.ts index 3e04eda8f0..00218f0b4a 100644 --- a/gallery/src/pages/misc/entity-state.ts +++ b/gallery/src/pages/misc/entity-state.ts @@ -130,6 +130,17 @@ const ENTITIES: HassEntity[] = [ createEntity("climate.auto", "auto"), createEntity("climate.dry", "dry"), createEntity("climate.fan_only", "fan_only"), + createEntity("climate.auto_idle", "auto", undefined, { hvac_action: "idle" }), + createEntity("climate.auto_off", "auto", undefined, { hvac_action: "off" }), + createEntity("climate.auto_heating", "auto", undefined, { + hvac_action: "heating", + }), + createEntity("climate.auto_cooling", "auto", undefined, { + hvac_action: "cooling", + }), + createEntity("climate.auto_dry", "auto", undefined, { + hvac_action: "drying", + }), // Cover createEntity("cover.closing", "closing"), createEntity("cover.closed", "closed"), diff --git a/src/common/entity/color/climate_color.ts b/src/common/entity/color/climate_color.ts index 5da4098a9b..9d7d17ef6a 100644 --- a/src/common/entity/color/climate_color.ts +++ b/src/common/entity/color/climate_color.ts @@ -1,3 +1,13 @@ +import { HvacAction } from "../../../data/climate"; + +export const CLIMATE_HVAC_ACTION_COLORS: Record = { + cooling: "var(--rgb-state-climate-cool-color)", + drying: "var(--rgb-state-climate-dry-color)", + heating: "var(--rgb-state-climate-heat-color)", + idle: "var(--rgb-state-climate-idle-color)", + off: "var(--rgb-state-climate-off-color)", +}; + export const climateColor = (state: string): string | undefined => { switch (state) { case "auto": diff --git a/src/components/entity/state-badge.ts b/src/components/entity/state-badge.ts index fdf507b0d0..abbcc52b73 100644 --- a/src/components/entity/state-badge.ts +++ b/src/components/entity/state-badge.ts @@ -11,6 +11,7 @@ import { import { property, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; import { styleMap } from "lit/directives/style-map"; +import { CLIMATE_HVAC_ACTION_COLORS } from "../../common/entity/color/climate_color"; import { computeDomain } from "../../common/entity/compute_domain"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { stateActive } from "../../common/entity/state_active"; @@ -34,7 +35,7 @@ export class StateBadge extends LitElement { @property({ type: Boolean, reflect: true, attribute: "icon" }) private _showIcon = true; - @state() private _iconStyle: { [name: string]: string } = {}; + @state() private _iconStyle: { [name: string]: string | undefined } = {}; private get _stateColor() { const domain = this.stateObj @@ -127,6 +128,14 @@ export class StateBadge extends LitElement { // lowest brightness will be around 50% (that's pretty dark) iconStyle.filter = `brightness(${(brightness + 245) / 5}%)`; } + if (stateObj.attributes.hvac_action) { + const hvacAction = stateObj.attributes.hvac_action; + if (["heating", "cooling", "drying"].includes(hvacAction)) { + iconStyle.color = `rgb(${CLIMATE_HVAC_ACTION_COLORS[hvacAction]})`; + } else { + delete iconStyle.color; + } + } } } else if (this.overrideImage) { let imageUrl = this.overrideImage; diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts index be8d189472..04eda69abe 100644 --- a/src/panels/lovelace/cards/hui-button-card.ts +++ b/src/panels/lovelace/cards/hui-button-card.ts @@ -21,6 +21,7 @@ import { ifDefined } from "lit/directives/if-defined"; import { styleMap } from "lit/directives/style-map"; import { DOMAINS_TOGGLE } from "../../../common/const"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; +import { CLIMATE_HVAC_ACTION_COLORS } from "../../../common/entity/color/climate_color"; import { computeDomain } from "../../../common/entity/compute_domain"; import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; @@ -316,12 +317,17 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { return ""; } - private _computeColor( - stateObj: HassEntity | LightEntity - ): string | undefined { + private _computeColor(stateObj: HassEntity): string | undefined { if (stateObj.attributes.rgb_color) { return `rgb(${stateObj.attributes.rgb_color.join(",")})`; } + if (stateObj.attributes.hvac_action) { + const hvacAction = stateObj.attributes.hvac_action; + if (["heating", "cooling", "drying"].includes(hvacAction)) { + return `rgb(${CLIMATE_HVAC_ACTION_COLORS[hvacAction]})`; + } + return undefined; + } const iconColor = stateColorCss(stateObj); if (iconColor) { return `rgb(${iconColor})`; diff --git a/src/panels/lovelace/cards/hui-entity-card.ts b/src/panels/lovelace/cards/hui-entity-card.ts index 3b73f770c9..ad00698ae6 100644 --- a/src/panels/lovelace/cards/hui-entity-card.ts +++ b/src/panels/lovelace/cards/hui-entity-card.ts @@ -12,6 +12,7 @@ import { ifDefined } from "lit/directives/if-defined"; import { styleMap } from "lit/directives/style-map"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { fireEvent } from "../../../common/dom/fire_event"; +import { CLIMATE_HVAC_ACTION_COLORS } from "../../../common/entity/color/climate_color"; import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { computeStateName } from "../../../common/entity/compute_state_name"; @@ -28,7 +29,6 @@ import "../../../components/ha-card"; import "../../../components/ha-icon"; import { UNAVAILABLE_STATES } from "../../../data/entity"; import { formatAttributeValue } from "../../../data/entity_attributes"; -import { LightEntity } from "../../../data/light"; import { HomeAssistant } from "../../../types"; import { computeCardSize } from "../common/compute-card-size"; import { findEntities } from "../common/find-entities"; @@ -193,9 +193,14 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { `; } - private _computeColor( - stateObj: HassEntity | LightEntity - ): string | undefined { + private _computeColor(stateObj: HassEntity): string | undefined { + if (stateObj.attributes.hvac_action) { + const hvacAction = stateObj.attributes.hvac_action; + if (["heating", "cooling", "drying"].includes(hvacAction)) { + return `rgb(${CLIMATE_HVAC_ACTION_COLORS[hvacAction]})`; + } + return undefined; + } const iconColor = stateColorCss(stateObj); if (iconColor) { return `rgb(${iconColor})`; diff --git a/src/panels/lovelace/cards/tile/badges/tile-badge-climate.ts b/src/panels/lovelace/cards/tile/badges/tile-badge-climate.ts index 547b8ef194..e06d24fae5 100644 --- a/src/panels/lovelace/cards/tile/badges/tile-badge-climate.ts +++ b/src/panels/lovelace/cards/tile/badges/tile-badge-climate.ts @@ -5,17 +5,10 @@ import { mdiSnowflake, mdiWaterPercent, } from "@mdi/js"; +import { CLIMATE_HVAC_ACTION_COLORS } from "../../../../../common/entity/color/climate_color"; import { ClimateEntity, HvacAction } from "../../../../../data/climate"; import { ComputeBadgeFunction } from "./tile-badge"; -export const CLIMATE_HVAC_ACTION_COLORS: Record = { - cooling: "var(--rgb-state-climate-cool-color)", - drying: "var(--rgb-state-climate-dry-color)", - heating: "var(--rgb-state-climate-heat-color)", - idle: "var(--rgb-state-climate-idle-color)", - off: "var(--rgb-state-climate-off-color)", -}; - export const CLIMATE_HVAC_ACTION_ICONS: Record = { cooling: mdiSnowflake, drying: mdiWaterPercent, diff --git a/src/resources/ha-style.ts b/src/resources/ha-style.ts index ab7b577356..56eced1a57 100644 --- a/src/resources/ha-style.ts +++ b/src/resources/ha-style.ts @@ -156,7 +156,7 @@ documentContainer.innerHTML = ` --rgb-state-climate-fan-only-color: var(--rgb-cyan-color); --rgb-state-climate-heat-color: var(--rgb-deep-orange-color); --rgb-state-climate-heat-cool-color: var(--rgb-amber-color); - --rgb-state-climate-idle-color: var(--rgb-off-color); + --rgb-state-climate-idle-color: var(--rgb-grey-color); --rgb-state-cover-color: var(--rgb-purple-color); --rgb-state-fan-color: var(--rgb-cyan-color); --rgb-state-group-color: var(--rgb-amber-color); From 1da8a974f8fb5bba9401c00a1f8ec6e99d20c0e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 13 Dec 2022 16:53:01 +0100 Subject: [PATCH 11/14] Do not try design preview on non-forks (#14753) --- .github/workflows/design_preview.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/design_preview.yaml b/.github/workflows/design_preview.yaml index a209613620..e53f0c83a3 100644 --- a/.github/workflows/design_preview.yaml +++ b/.github/workflows/design_preview.yaml @@ -17,7 +17,9 @@ env: jobs: preview: runs-on: ubuntu-latest - if: contains(github.event.pull_request.labels.*.name, 'needs design preview') + # Skip running on forks since it won't have access to secrets + # Skip running PRs without 'needs design preview' label + if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview') steps: - name: Check out files from GitHub uses: actions/checkout@v3 From f7aecb0d6df0d042a2f82a7daa17f638d2273f4f Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 13 Dec 2022 17:01:59 +0100 Subject: [PATCH 12/14] Move groups up, after areas, move devices down (#14741) --- .../common/generate-lovelace-config.ts | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/panels/lovelace/common/generate-lovelace-config.ts b/src/panels/lovelace/common/generate-lovelace-config.ts index ffbcd951da..12dd3fc537 100644 --- a/src/panels/lovelace/common/generate-lovelace-config.ts +++ b/src/panels/lovelace/common/generate-lovelace-config.ts @@ -304,19 +304,13 @@ export const generateViewConfig = ( path: string, title: string | undefined, icon: string | undefined, - entities: HassEntities, - groupOrders: { [entityId: string]: number } + entities: HassEntities ): LovelaceViewConfig => { - const splitted = splitByGroups(entities); - splitted.groups.sort( - (gr1, gr2) => groupOrders[gr1.entity_id] - groupOrders[gr2.entity_id] - ); - const ungroupedEntitites: { [domain: string]: string[] } = {}; // Organize ungrouped entities in ungrouped things - for (const entityId of Object.keys(splitted.ungrouped)) { - const state = splitted.ungrouped[entityId]; + for (const entityId of Object.keys(entities)) { + const state = entities[entityId]; const domain = computeStateDomain(state); if (!(domain in ungroupedEntitites)) { @@ -384,15 +378,6 @@ export const generateViewConfig = ( delete ungroupedEntitites.person; } - for (const groupEntity of splitted.groups) { - cards.push( - ...computeCards(entities, groupEntity.attributes.entity_id, { - title: computeStateName(groupEntity), - show_header_toggle: groupEntity.attributes.control !== "hidden", - }) - ); - } - // Group helper entities in a single card const helperEntities: string[] = []; @@ -480,13 +465,28 @@ export const generateDefaultViewConfig = ( states ); + const splittedByGroups = splitByGroups(splittedByAreaDevice.otherEntities); + splittedByGroups.groups.sort( + (gr1, gr2) => groupOrders[gr1.entity_id] - groupOrders[gr2.entity_id] + ); + + const groupCards: LovelaceCardConfig[] = []; + + for (const groupEntity of splittedByGroups.groups) { + groupCards.push( + ...computeCards(entities, groupEntity.attributes.entity_id, { + title: computeStateName(groupEntity), + show_header_toggle: groupEntity.attributes.control !== "hidden", + }) + ); + } + const config = generateViewConfig( localize, path, title, icon, - splittedByAreaDevice.otherEntities, - groupOrders + splittedByGroups.ungrouped ); const areaCards: LovelaceCardConfig[] = []; @@ -568,9 +568,11 @@ export const generateDefaultViewConfig = ( config.cards!.unshift( ...areaCards, - ...(energyCard ? [energyCard] : []), - ...deviceCards + ...groupCards, + ...(energyCard ? [energyCard] : []) ); + config.cards!.push(...deviceCards); + return config; }; From f2b7288e923cf8a5b9054cbb36d8f7b2e62f1291 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 13 Dec 2022 17:22:37 +0100 Subject: [PATCH 13/14] Bumped version to 20221213.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2c4a530952..5641dfcf15 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20221212.0" +version = "20221213.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 9c27bb37a0a4e12fe4a4ee54912fba653ac40742 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Tue, 13 Dec 2022 17:23:49 +0100 Subject: [PATCH 14/14] Ensure consistent light state icon brightness (#14740) * Ensure consistent light state icon brightness * Update hui-entity-card.ts * Update hui-entity-card.ts Co-authored-by: Paul Bottein --- src/panels/lovelace/cards/hui-button-card.ts | 6 ++++-- src/panels/lovelace/cards/hui-entity-card.ts | 13 +++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts index 04eda69abe..6a058ddb24 100644 --- a/src/panels/lovelace/cards/hui-button-card.ts +++ b/src/panels/lovelace/cards/hui-button-card.ts @@ -193,8 +193,10 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { .icon=${this._config.icon} .state=${stateObj} style=${styleMap({ - color: colored ? this._computeColor(stateObj) : "", - filter: colored ? this._computeBrightness(stateObj) : "", + color: colored ? this._computeColor(stateObj) : undefined, + filter: colored + ? this._computeBrightness(stateObj) + : undefined, height: this._config.icon_height ? this._config.icon_height : "", diff --git a/src/panels/lovelace/cards/hui-entity-card.ts b/src/panels/lovelace/cards/hui-entity-card.ts index ad00698ae6..401c689bd5 100644 --- a/src/panels/lovelace/cards/hui-entity-card.ts +++ b/src/panels/lovelace/cards/hui-entity-card.ts @@ -29,6 +29,7 @@ import "../../../components/ha-card"; import "../../../components/ha-icon"; import { UNAVAILABLE_STATES } from "../../../data/entity"; import { formatAttributeValue } from "../../../data/entity_attributes"; +import { LightEntity } from "../../../data/light"; import { HomeAssistant } from "../../../types"; import { computeCardSize } from "../common/compute-card-size"; import { findEntities } from "../common/find-entities"; @@ -149,6 +150,7 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { data-state=${stateObj.state} style=${styleMap({ color: colored ? this._computeColor(stateObj) : undefined, + filter: colored ? this._computeBrightness(stateObj) : undefined, height: this._config.icon_height ? this._config.icon_height : "", @@ -201,6 +203,9 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { } return undefined; } + if (stateObj.attributes.rgb_color && stateActive(stateObj)) { + return `rgb(${stateObj.attributes.rgb_color.join(",")})`; + } const iconColor = stateColorCss(stateObj); if (iconColor) { return `rgb(${iconColor})`; @@ -208,6 +213,14 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { return undefined; } + private _computeBrightness(stateObj: HassEntity | LightEntity): string { + if (stateObj.attributes.brightness && stateActive(stateObj)) { + const brightness = stateObj.attributes.brightness; + return `brightness(${(brightness + 245) / 5}%)`; + } + return ""; + } + protected shouldUpdate(changedProps: PropertyValues): boolean { // Side Effect used to update footer hass while keeping optimizations if (this._footerElement) {