Skip to content

Commit

Permalink
20241127.1 (#23045)
Browse files Browse the repository at this point in the history
  • Loading branch information
bramkragten authored Nov 28, 2024
2 parents 91d5d2f + f6c15dc commit 6e00390
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 83 deletions.
1 change: 0 additions & 1 deletion build-scripts/gulp/locale-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const outDir = join(paths.build_dir, "locale-data");

const INTL_POLYFILLS = {
"intl-datetimeformat": "DateTimeFormat",
"intl-durationFormat": "DurationFormat",
"intl-displaynames": "DisplayNames",
"intl-listformat": "ListFormat",
"intl-numberformat": "NumberFormat",
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "home-assistant-frontend"
version = "20241127.0"
version = "20241127.1"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"
Expand Down
30 changes: 7 additions & 23 deletions src/common/datetime/format_duration.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { DurationFormat } from "@formatjs/intl-durationformat";
import type { DurationInput } from "@formatjs/intl-durationformat/src/types";
import memoizeOne from "memoize-one";
import type { HaDurationData } from "../../components/ha-duration-input";
Expand Down Expand Up @@ -49,7 +48,7 @@ export const formatNumericDuration = (

const formatDurationLongMem = memoizeOne(
(locale: FrontendLocaleData) =>
new DurationFormat(locale.language, {
new Intl.DurationFormat(locale.language, {
style: "long",
})
);
Expand All @@ -61,7 +60,7 @@ export const formatDurationLong = (

const formatDigitalDurationMem = memoizeOne(
(locale: FrontendLocaleData) =>
new DurationFormat(locale.language, {
new Intl.DurationFormat(locale.language, {
style: "digital",
hoursDisplay: "auto",
})
Expand All @@ -72,50 +71,42 @@ export const formatDurationDigital = (
duration: HaDurationData
) => formatDigitalDurationMem(locale).format(duration);

export const DURATION_UNITS = ["ms", "s", "min", "h", "d"] as const;
export const DURATION_UNITS = ["s", "min", "h", "d"] as const;

type DurationUnit = (typeof DURATION_UNITS)[number];

const formatDurationDayMem = memoizeOne(
(locale: FrontendLocaleData) =>
new DurationFormat(locale.language, {
new Intl.DurationFormat(locale.language, {
style: "narrow",
daysDisplay: "always",
})
);

const formatDurationHourMem = memoizeOne(
(locale: FrontendLocaleData) =>
new DurationFormat(locale.language, {
new Intl.DurationFormat(locale.language, {
style: "narrow",
hoursDisplay: "always",
})
);

const formatDurationMinuteMem = memoizeOne(
(locale: FrontendLocaleData) =>
new DurationFormat(locale.language, {
new Intl.DurationFormat(locale.language, {
style: "narrow",
minutesDisplay: "always",
})
);

const formatDurationSecondMem = memoizeOne(
(locale: FrontendLocaleData) =>
new DurationFormat(locale.language, {
new Intl.DurationFormat(locale.language, {
style: "narrow",
secondsDisplay: "always",
})
);

const formatDurationMillisecondMem = memoizeOne(
(locale: FrontendLocaleData) =>
new DurationFormat(locale.language, {
style: "narrow",
millisecondsDisplay: "always",
})
);

export const formatDuration = (
locale: FrontendLocaleData,
duration: string,
Expand Down Expand Up @@ -164,13 +155,6 @@ export const formatDuration = (
};
return formatDurationSecondMem(locale).format(input);
}
case "ms": {
const milliseconds = Math.floor(value);
const input: DurationInput = {
milliseconds,
};
return formatDurationMillisecondMem(locale).format(input);
}
default:
throw new Error("Invalid duration unit");
}
Expand Down
9 changes: 5 additions & 4 deletions src/data/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ export interface IntegrationManifest {
loggers?: string[];
quality_scale?:
| "bronze"
| "silver"
| "gold"
| "internal"
| "platinum"
| "silver"
| "custom"
| "no_score";
| "no_score"
| "internal"
| "legacy"
| "custom";
iot_class:
| "assumed_state"
| "cloud_polling"
Expand Down
39 changes: 39 additions & 0 deletions src/data/integration_quality_scale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { mdiContentSave, mdiMedal, mdiTrophy } from "@mdi/js";
import { mdiHomeAssistant } from "../resources/home-assistant-logo-svg";
import type { LocalizeKeys } from "../common/translations/localize";

/**
* Map integration quality scale to icon and translation key.
*/
export const QUALITY_SCALE_MAP: Record<
string,
{ icon: string; translationKey: LocalizeKeys }
> = {
bronze: {
icon: mdiMedal,
translationKey: "ui.panel.config.integrations.config_entry.bronze_quality",
},
silver: {
icon: mdiMedal,
translationKey: "ui.panel.config.integrations.config_entry.silver_quality",
},
gold: {
icon: mdiMedal,
translationKey: "ui.panel.config.integrations.config_entry.gold_quality",
},
platinum: {
icon: mdiTrophy,
translationKey:
"ui.panel.config.integrations.config_entry.platinum_quality",
},
internal: {
icon: mdiHomeAssistant,
translationKey:
"ui.panel.config.integrations.config_entry.internal_integration",
},
legacy: {
icon: mdiContentSave,
translationKey:
"ui.panel.config.integrations.config_entry.legacy_integration",
},
};
81 changes: 44 additions & 37 deletions src/panels/config/integrations/ha-config-integration-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
mdiDownload,
mdiFileCodeOutline,
mdiHandExtendedOutline,
mdiMedal,
mdiOpenInNew,
mdiPackageVariant,
mdiPlayCircleOutline,
Expand All @@ -23,7 +22,6 @@ import {
mdiRenameBox,
mdiShapeOutline,
mdiStopCircleOutline,
mdiTrophy,
mdiWeb,
mdiWrench,
} from "@mdi/js";
Expand Down Expand Up @@ -107,9 +105,7 @@ import { documentationUrl } from "../../../util/documentation-url";
import { fileDownload } from "../../../util/file_download";
import type { DataEntryFlowProgressExtended } from "./ha-config-integrations";
import { showAddIntegrationDialog } from "./show-add-integration-dialog";

type MedalColor = "gold" | "silver" | "bronze" | "platinum";
const MEDAL_COLORS = ["bronze", "silver", "gold", "platinum"];
import { QUALITY_SCALE_MAP } from "../../../data/integration_quality_scale";

export const renderConfigEntryError = (
hass: HomeAssistant,
Expand Down Expand Up @@ -344,36 +340,30 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
? html`<div class="version">${this._manifest.version}</div>`
: nothing}
${this._manifest?.quality_scale &&
MEDAL_COLORS.includes(this._manifest.quality_scale)
Object.keys(QUALITY_SCALE_MAP).includes(
this._manifest.quality_scale
)
? html`
<div class="quality-scale integration-info">
<ha-svg-icon
class=${`${this._manifest.quality_scale}-medal`}
.path=${this._manifest.quality_scale === "platinum"
? mdiTrophy
: mdiMedal}
class=${`${this._manifest.quality_scale}-quality`}
.path=${QUALITY_SCALE_MAP[
this._manifest.quality_scale
].icon}
></ha-svg-icon>
<span>
<a
href=${documentationUrl(
this.hass,
`/docs/quality_scale/#-${this._manifest.quality_scale}`
)}
rel="noopener noreferrer"
target="_blank"
>
${this.hass.localize(
`ui.panel.config.integrations.config_entry.${this._manifest.quality_scale as MedalColor}_quality`,
{
quality_scale: html`
<a
href=${documentationUrl(
this.hass,
`/docs/quality_scale/#${this._manifest.quality_scale}-`
)}
rel="noopener noreferrer"
target="_blank"
>
${this.hass.localize(
"ui.panel.config.integrations.config_entry.quality_scale"
)}
</a>
`,
}
QUALITY_SCALE_MAP[this._manifest.quality_scale]
.translationKey
)}
</span>
</a>
</div>
`
: nothing}
Expand All @@ -383,9 +373,18 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
class="warning"
path=${mdiPackageVariant}
></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.integrations.config_entry.custom_integration"
)}
<a
href=${documentationUrl(
this.hass,
`/docs/quality_scale/#-custom`
)}
rel="noopener noreferrer"
target="_blank"
>
${this.hass.localize(
"ui.panel.config.integrations.config_entry.custom_integration"
)}
</a>
</div>`
: nothing}
${this._manifest?.iot_class?.startsWith("cloud_")
Expand Down Expand Up @@ -1496,6 +1495,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
.logo-container {
display: flex;
justify-content: center;
margin-bottom: 8px;
}
.version {
padding-top: 8px;
Expand Down Expand Up @@ -1538,17 +1538,24 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
100%;
animation: shimmer 2.5s infinite;
}
ha-svg-icon.bronze-medal {
ha-svg-icon.bronze-quality {
color: #cd7f32;
}
ha-svg-icon.silver-medal {
ha-svg-icon.silver-quality {
color: silver;
}
ha-svg-icon.gold-medal {
ha-svg-icon.gold-quality {
color: gold;
}
ha-svg-icon.platinum-medal {
color: #d9d9d9;
ha-svg-icon.platinum-quality {
color: #727272;
}
ha-svg-icon.internal-quality {
color: var(--primary-color);
}
ha-svg-icon.legacy-quality {
color: var(--mdc-theme-text-icon-on-background, rgba(0, 0, 0, 0.38));
animation: unset;
}
ha-md-list-item {
position: relative;
Expand Down
13 changes: 7 additions & 6 deletions src/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -4505,8 +4505,10 @@
}
},
"custom_integration": "Custom integration",
"internal_integration": "Internal integration",
"legacy_integration": "Legacy integration",
"custom_overwrites_core": "Custom integration that replaces a core component",
"depends_on_cloud": "Depends on Internet connection",
"depends_on_cloud": "Requires Internet",
"yaml_only": "This integration cannot be setup from the UI",
"no_config_flow": "This integration was not set up from the UI",
"disabled_polling": "Automatic polling for updated data disabled",
Expand All @@ -4521,11 +4523,10 @@
"setup_in_progress": "Initializing"
},
"open_configuration_url": "Visit device",
"bronze_quality": "Bronze on our {quality_scale}",
"silver_quality": "Silver on our {quality_scale}",
"gold_quality": "Gold on our {quality_scale}",
"platinum_quality": "Platinum on our {quality_scale}",
"quality_scale": "quality scale"
"bronze_quality": "Bronze quality",
"silver_quality": "Silver quality",
"gold_quality": "Gold quality",
"platinum_quality": "Platinum quality"
},
"config_flow": {
"success": "Success",
Expand Down
11 changes: 9 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { DurationFormatConstructor } from "@formatjs/intl-durationformat/src/types";
import type {
Auth,
Connection,
Expand All @@ -22,15 +23,15 @@ import type { Themes } from "./data/ws-themes";
import type { ExternalMessaging } from "./external_app/external_messaging";

declare global {
/* eslint-disable no-var, no-redeclare */
/* eslint-disable no-var */
var __DEV__: boolean;
var __DEMO__: boolean;
var __BUILD__: "modern" | "legacy";
var __VERSION__: string;
var __STATIC_PATH__: string;
var __BACKWARDS_COMPAT__: boolean;
var __SUPERVISOR__: boolean;
/* eslint-enable no-var, no-redeclare */
/* eslint-enable no-var */

interface Window {
// Custom panel entry point url
Expand Down Expand Up @@ -64,6 +65,12 @@ declare global {
interface ImportMeta {
url: string;
}

// Intl.DurationFormat is not yet part of the TypeScript standard
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Intl {
const DurationFormat: DurationFormatConstructor;
}
}

export interface ValueChangedEvent<T> extends CustomEvent {
Expand Down
10 changes: 1 addition & 9 deletions test/common/datetime/format_duration.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "@formatjs/intl-durationformat/polyfill-force";
import { assert, describe, it } from "vitest";

import { formatDuration } from "../../../src/common/datetime/format_duration";
import type { FrontendLocaleData } from "../../../src/data/translation";
import {
Expand All @@ -21,14 +21,6 @@ const LOCALE: FrontendLocaleData = {

describe("formatDuration", () => {
it("works", () => {
assert.strictEqual(formatDuration(LOCALE, "0", "ms"), "0ms");
assert.strictEqual(formatDuration(LOCALE, "1", "ms"), "1ms");
assert.strictEqual(formatDuration(LOCALE, "10", "ms"), "10ms");
assert.strictEqual(formatDuration(LOCALE, "100", "ms"), "100ms");
assert.strictEqual(formatDuration(LOCALE, "1000", "ms"), "1,000ms");
assert.strictEqual(formatDuration(LOCALE, "1001", "ms"), "1,001ms");
assert.strictEqual(formatDuration(LOCALE, "65000", "ms"), "65,000ms");

assert.strictEqual(formatDuration(LOCALE, "0.5", "s"), "0s 500ms");
assert.strictEqual(formatDuration(LOCALE, "1", "s"), "1s");
assert.strictEqual(formatDuration(LOCALE, "1.1", "s"), "1s 100ms");
Expand Down

0 comments on commit 6e00390

Please sign in to comment.