diff --git a/package.json b/package.json index 2a2a12d83b..581e6eb9d8 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "@webcomponents/scoped-custom-element-registry": "0.0.9", "@webcomponents/webcomponentsjs": "2.8.0", "app-datepicker": "5.1.1", - "chart.js": "4.4.0", + "chart.js": "4.4.1", "comlink": "4.4.1", "core-js": "3.33.3", "cropperjs": "1.6.1", @@ -176,7 +176,7 @@ "@types/js-yaml": "4.0.9", "@types/leaflet": "1.9.8", "@types/leaflet-draw": "1.0.11", - "@types/luxon": "3.3.6", + "@types/luxon": "3.3.7", "@types/mocha": "10.0.6", "@types/qrcode": "1.5.5", "@types/serve-handler": "6.1.4", @@ -184,8 +184,8 @@ "@types/tar": "6.1.10", "@types/ua-parser-js": "0.7.39", "@types/webspeechapi": "0.0.29", - "@typescript-eslint/eslint-plugin": "6.13.1", - "@typescript-eslint/parser": "6.13.1", + "@typescript-eslint/eslint-plugin": "6.13.2", + "@typescript-eslint/parser": "6.13.2", "@web/dev-server": "0.1.38", "@web/dev-server-rollup": "0.4.1", "babel-loader": "9.1.3", @@ -216,7 +216,7 @@ "husky": "8.0.3", "instant-mocha": "1.5.2", "jszip": "3.10.1", - "lint-staged": "15.1.0", + "lint-staged": "15.2.0", "lit-analyzer": "2.0.1", "lodash.template": "4.5.0", "magic-string": "0.30.5", @@ -229,7 +229,7 @@ "rollup": "2.79.1", "rollup-plugin-string": "3.0.0", "rollup-plugin-terser": "7.0.2", - "rollup-plugin-visualizer": "5.9.3", + "rollup-plugin-visualizer": "5.10.0", "serve-handler": "6.1.5", "sinon": "17.0.1", "source-map-url": "0.4.1", diff --git a/pyproject.toml b/pyproject.toml index 72f8dec3af..6267bdc101 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20231206.0" +version = "20231208.1" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" diff --git a/src/common/entity/compute_attribute_display.ts b/src/common/entity/compute_attribute_display.ts index bfb394a2d0..77bc61bbd4 100644 --- a/src/common/entity/compute_attribute_display.ts +++ b/src/common/entity/compute_attribute_display.ts @@ -1,5 +1,6 @@ import { HassConfig, HassEntity } from "home-assistant-js-websocket"; import { + DOMAIN_ATTRIBUTES_FORMATERS, DOMAIN_ATTRIBUTES_UNITS, TEMPERATURE_ATTRIBUTES, } from "../../data/entity_attributes"; @@ -14,11 +15,10 @@ import { formatNumber } from "../number/format_number"; import { capitalizeFirstLetter } from "../string/capitalize-first-letter"; import { isDate } from "../string/is_date"; import { isTimestamp } from "../string/is_timestamp"; -import { blankBeforePercent } from "../translations/blank_before_percent"; +import { blankBeforeUnit } from "../translations/blank_before_unit"; import { LocalizeFunc } from "../translations/localize"; import { computeDomain } from "./compute_domain"; import { computeStateDomain } from "./compute_state_domain"; -import { blankBeforeUnit } from "../translations/blank_before_unit"; export const computeAttributeValueDisplay = ( localize: LocalizeFunc, @@ -39,19 +39,18 @@ export const computeAttributeValueDisplay = ( // Number value, return formatted number if (typeof attributeValue === "number") { - const formattedValue = formatNumber(attributeValue, locale); - const domain = computeStateDomain(stateObj); + const formatter = DOMAIN_ATTRIBUTES_FORMATERS[domain]?.[attribute]; + + const formattedValue = formatter + ? formatter(attributeValue, locale) + : formatNumber(attributeValue, locale); + let unit = DOMAIN_ATTRIBUTES_UNITS[domain]?.[attribute] as | string | undefined; - if (domain === "light" && attribute === "brightness") { - const percentage = Math.round((attributeValue / 255) * 100); - return `${percentage}${blankBeforePercent(locale)}%`; - } - if (domain === "weather") { unit = getWeatherUnit(config, stateObj as WeatherEntity, attribute); } diff --git a/src/components/device/ha-device-picker.ts b/src/components/device/ha-device-picker.ts index dd64cf68c2..35e16fd4ea 100644 --- a/src/components/device/ha-device-picker.ts +++ b/src/components/device/ha-device-picker.ts @@ -10,7 +10,6 @@ import { ScorableTextItem, fuzzyFilterSort, } from "../../common/string/filter/sequence-matching"; -import { AreaRegistryEntry } from "../../data/area_registry"; import { DeviceEntityDisplayLookup, DeviceRegistryEntry, @@ -102,7 +101,7 @@ export class HaDevicePicker extends LitElement { private _getDevices = memoizeOne( ( devices: DeviceRegistryEntry[], - areas: AreaRegistryEntry[], + areas: HomeAssistant["areas"], entities: EntityRegistryDisplayEntry[], includeDomains: this["includeDomains"], excludeDomains: this["excludeDomains"], @@ -133,8 +132,6 @@ export class HaDevicePicker extends LitElement { deviceEntityLookup = getDeviceEntityDisplayLookup(entities); } - const areaLookup = areas; - let inputDevices = devices.filter( (device) => device.id === this.value || !device.disabled_by ); @@ -224,8 +221,8 @@ export class HaDevicePicker extends LitElement { id: device.id, name: name, area: - device.area_id && areaLookup[device.area_id] - ? areaLookup[device.area_id].name + device.area_id && areas[device.area_id] + ? areas[device.area_id].name : this.hass.localize("ui.components.device-picker.no_area"), strings: [name || ""], }; @@ -267,7 +264,7 @@ export class HaDevicePicker extends LitElement { this._init = true; const devices = this._getDevices( Object.values(this.hass.devices), - Object.values(this.hass.areas), + this.hass.areas, Object.values(this.hass.entities), this.includeDomains, this.excludeDomains, diff --git a/src/components/ha-selector/ha-selector-number.ts b/src/components/ha-selector/ha-selector-number.ts index 1e913e09b4..3bc9451e18 100644 --- a/src/components/ha-selector/ha-selector-number.ts +++ b/src/components/ha-selector/ha-selector-number.ts @@ -30,9 +30,9 @@ export class HaNumberSelector extends LitElement { protected willUpdate(changedProps: PropertyValues) { if (changedProps.has("value")) { - if (this.value !== Number(this._valueStr)) { + if (this._valueStr === "" || this.value !== Number(this._valueStr)) { this._valueStr = - !this.value || isNaN(this.value) ? "" : this.value.toString(); + this.value == null || isNaN(this.value) ? "" : this.value.toString(); } } } diff --git a/src/data/entity_attributes.ts b/src/data/entity_attributes.ts index 00e2e6d3dc..3a07ae022a 100644 --- a/src/data/entity_attributes.ts +++ b/src/data/entity_attributes.ts @@ -1,3 +1,6 @@ +import { formatDuration } from "../common/datetime/duration"; +import { FrontendLocaleData } from "./translation"; + export const STATE_ATTRIBUTES = [ "entity_id", "assumed_state", @@ -64,6 +67,7 @@ export const DOMAIN_ATTRIBUTES_UNITS = { color_temp_kelvin: "K", min_color_temp_kelvin: "K", max_color_temp_kelvin: "K", + brightness: "%", }, sun: { elevation: "°", @@ -74,4 +78,22 @@ export const DOMAIN_ATTRIBUTES_UNITS = { sensor: { battery_level: "%", }, + media_player: { + volume_level: "%", + }, } as const satisfies Record>; + +type Formatter = (value: number, locale: FrontendLocaleData) => string; + +export const DOMAIN_ATTRIBUTES_FORMATERS: Record< + string, + Record +> = { + light: { + brightness: (value) => Math.round((value / 255) * 100).toString(), + }, + media_player: { + volume_level: (value) => Math.round(value * 100).toString(), + media_duration: (value) => formatDuration(value.toString(), "s"), + }, +}; diff --git a/src/dialogs/config-flow/dialog-data-entry-flow.ts b/src/dialogs/config-flow/dialog-data-entry-flow.ts index e80eedd321..8fd26b47cb 100644 --- a/src/dialogs/config-flow/dialog-data-entry-flow.ts +++ b/src/dialogs/config-flow/dialog-data-entry-flow.ts @@ -425,13 +425,6 @@ class DataEntryFlowDialog extends LitElement { ); } ); - if (this._step?.flow_id) { - await this._unsubDataEntryFlowProgressed; - // fetch flow after we subscribe to the event, so we don't miss the first event - this._processStep( - this._params!.flowConfig.fetchFlow(this.hass, this._step.flow_id) - ); - } } static get styles(): CSSResultGroup { diff --git a/src/html/authorize.html.template b/src/html/authorize.html.template index 11724890bf..d4275d3381 100644 --- a/src/html/authorize.html.template +++ b/src/html/authorize.html.template @@ -26,7 +26,8 @@ width: 100%; max-width: 400px; margin: 0 auto; - box-sizing: border-box; + padding: 0 16px; + box-sizing: content-box; } .header { @@ -40,12 +41,6 @@ height: 56px; width: 56px; } - - @media (max-width: 592px) { - .content { - margin: 0 16px; - } - } diff --git a/src/html/onboarding.html.template b/src/html/onboarding.html.template index 9f89da79f0..cac5129033 100644 --- a/src/html/onboarding.html.template +++ b/src/html/onboarding.html.template @@ -22,7 +22,8 @@ .content { max-width: 560px; margin: 0 auto; - box-sizing: border-box; + padding: 0 16px; + box-sizing: content-box; } .header { @@ -36,12 +37,6 @@ height: 56px; width: 56px; } - - @media (max-width: 592px) { - .content { - margin: 0 16px; - } - } diff --git a/src/panels/lovelace/cards/hui-tile-card.ts b/src/panels/lovelace/cards/hui-tile-card.ts index 101c983eeb..f20eb2ecde 100644 --- a/src/panels/lovelace/cards/hui-tile-card.ts +++ b/src/panels/lovelace/cards/hui-tile-card.ts @@ -310,24 +310,22 @@ export class HuiTileCard extends LitElement implements LovelaceCard { if (!stateObj) { return html` -
-
-
- - -
- +
+
+ +
+
`; @@ -359,73 +357,71 @@ export class HuiTileCard extends LitElement implements LovelaceCard { return html` -
+
+ ${this._shouldRenderRipple + ? html`` + : nothing} +
+
- ${this._shouldRenderRipple - ? html`` + ${imageUrl + ? html` + + ` + : html` + + `} + ${badge + ? html` + + ` : nothing}
-
-
- ${imageUrl - ? html` - - ` - : html` - - `} - ${badge - ? html` - - ` - : nothing} -
- -
+