mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
20240703.0 (#21264)
This commit is contained in:
commit
58ba9f628a
@ -8,6 +8,7 @@
|
|||||||
"postCreateCommand": "sudo apt update && sudo apt upgrade -y && sudo apt install -y libpcap-dev",
|
"postCreateCommand": "sudo apt update && sudo apt upgrade -y && sudo apt install -y libpcap-dev",
|
||||||
"postStartCommand": "script/bootstrap",
|
"postStartCommand": "script/bootstrap",
|
||||||
"containerEnv": {
|
"containerEnv": {
|
||||||
|
"DEV_CONTAINER": "1",
|
||||||
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}"
|
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}"
|
||||||
},
|
},
|
||||||
"customizations": {
|
"customizations": {
|
||||||
|
@ -32,4 +32,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
return version[1];
|
return version[1];
|
||||||
},
|
},
|
||||||
|
isDevContainer() {
|
||||||
|
return process.env.DEV_CONTAINER === "1";
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -40,8 +40,12 @@ const runDevServer = async ({
|
|||||||
compiler,
|
compiler,
|
||||||
contentBase,
|
contentBase,
|
||||||
port,
|
port,
|
||||||
listenHost = "localhost",
|
listenHost = undefined,
|
||||||
}) => {
|
}) => {
|
||||||
|
if (listenHost === undefined) {
|
||||||
|
// For dev container, we need to listen on all hosts
|
||||||
|
listenHost = env.isDevContainer() ? "0.0.0.0" : "localhost";
|
||||||
|
}
|
||||||
const server = new WebpackDevServer(
|
const server = new WebpackDevServer(
|
||||||
{
|
{
|
||||||
hot: false,
|
hot: false,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import "../../../src/resources/safari-14-attachshadow-patch";
|
|
||||||
import "./layout/hc-connect";
|
import "./layout/hc-connect";
|
||||||
|
|
||||||
import("../../../src/resources/ha-style");
|
import("../../../src/resources/ha-style");
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import "../../src/resources/safari-14-attachshadow-patch";
|
|
||||||
import "./util/is_frontpage";
|
import "./util/is_frontpage";
|
||||||
import "./ha-demo";
|
import "./ha-demo";
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// Compat needs to be first import
|
// Compat needs to be first import
|
||||||
import "../../src/resources/compatibility";
|
import "../../src/resources/compatibility";
|
||||||
import "../../src/resources/safari-14-attachshadow-patch";
|
|
||||||
import "./hassio-main";
|
import "./hassio-main";
|
||||||
|
|
||||||
import("../../src/resources/ha-style");
|
import("../../src/resources/ha-style");
|
||||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "home-assistant-frontend"
|
name = "home-assistant-frontend"
|
||||||
version = "20240702.0"
|
version = "20240703.0"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "The Home Assistant frontend"
|
description = "The Home Assistant frontend"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -202,20 +202,53 @@ export class DialogDataTableSettings extends LitElement {
|
|||||||
const columns = this._sortedColumns(
|
const columns = this._sortedColumns(
|
||||||
this._params.columns,
|
this._params.columns,
|
||||||
this._columnOrder,
|
this._columnOrder,
|
||||||
this._hiddenColumns
|
hidden
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!this._columnOrder) {
|
if (!this._columnOrder) {
|
||||||
this._columnOrder = columns.map((col) => col.key);
|
this._columnOrder = columns.map((col) => col.key);
|
||||||
} else {
|
} else {
|
||||||
|
const newOrder = this._columnOrder.filter((col) => col !== column);
|
||||||
|
|
||||||
|
// Array.findLastIndex when supported or core-js polyfill
|
||||||
|
const findLastIndex = (
|
||||||
|
arr: Array<any>,
|
||||||
|
fn: (item: any, index: number, arr: Array<any>) => boolean
|
||||||
|
) => {
|
||||||
|
for (let i = arr.length - 1; i >= 0; i--) {
|
||||||
|
if (fn(arr[i], i, arr)) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
let lastMoveable = findLastIndex(
|
||||||
|
newOrder,
|
||||||
|
(col) =>
|
||||||
|
col !== column &&
|
||||||
|
!hidden.includes(col) &&
|
||||||
|
!this._params!.columns[col].main &&
|
||||||
|
this._params!.columns[col].moveable !== false
|
||||||
|
);
|
||||||
|
|
||||||
|
if (lastMoveable === -1) {
|
||||||
|
lastMoveable = newOrder.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
columns.forEach((col) => {
|
columns.forEach((col) => {
|
||||||
if (!this._columnOrder!.includes(col.key)) {
|
if (!newOrder.includes(col.key)) {
|
||||||
this._columnOrder!.push(col.key);
|
if (col.moveable === false) {
|
||||||
|
newOrder.unshift(col.key);
|
||||||
|
} else {
|
||||||
|
newOrder.splice(lastMoveable + 1, 0, col.key);
|
||||||
|
}
|
||||||
|
|
||||||
if (col.defaultHidden) {
|
if (col.defaultHidden) {
|
||||||
hidden.push(col.key);
|
hidden.push(col.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._columnOrder = newOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._hiddenColumns = hidden;
|
this._hiddenColumns = hidden;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Compat needs to be first import
|
// Compat needs to be first import
|
||||||
import "../resources/compatibility";
|
import "../resources/compatibility";
|
||||||
import "../auth/ha-authorize";
|
import "../auth/ha-authorize";
|
||||||
import "../resources/safari-14-attachshadow-patch";
|
|
||||||
|
|
||||||
import("../resources/ha-style");
|
import("../resources/ha-style");
|
||||||
import("@polymer/polymer/lib/utils/settings").then(
|
import("@polymer/polymer/lib/utils/settings").then(
|
||||||
|
@ -25,7 +25,6 @@ import { subscribePanels } from "../data/ws-panels";
|
|||||||
import { subscribeThemes } from "../data/ws-themes";
|
import { subscribeThemes } from "../data/ws-themes";
|
||||||
import { subscribeUser } from "../data/ws-user";
|
import { subscribeUser } from "../data/ws-user";
|
||||||
import type { ExternalAuth } from "../external_app/external_auth";
|
import type { ExternalAuth } from "../external_app/external_auth";
|
||||||
import "../resources/safari-14-attachshadow-patch";
|
|
||||||
|
|
||||||
window.name = MAIN_WINDOW_NAME;
|
window.name = MAIN_WINDOW_NAME;
|
||||||
(window as any).frontendVersion = __VERSION__;
|
(window as any).frontendVersion = __VERSION__;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// Compat needs to be first import
|
// Compat needs to be first import
|
||||||
import "../resources/compatibility";
|
import "../resources/compatibility";
|
||||||
import "../resources/safari-14-attachshadow-patch";
|
|
||||||
|
|
||||||
import { CSSResult } from "lit";
|
import { CSSResult } from "lit";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Compat needs to be first import
|
// Compat needs to be first import
|
||||||
import "../resources/compatibility";
|
import "../resources/compatibility";
|
||||||
import "../onboarding/ha-onboarding";
|
import "../onboarding/ha-onboarding";
|
||||||
import "../resources/safari-14-attachshadow-patch";
|
|
||||||
|
|
||||||
import("../resources/ha-style");
|
import("../resources/ha-style");
|
||||||
import("@polymer/polymer/lib/utils/settings").then(
|
import("@polymer/polymer/lib/utils/settings").then(
|
||||||
|
@ -10,6 +10,18 @@ const now = () => new Date().toISOString();
|
|||||||
const randomTime = () =>
|
const randomTime = () =>
|
||||||
new Date(new Date().getTime() - Math.random() * 80 * 60 * 1000).toISOString();
|
new Date(new Date().getTime() - Math.random() * 80 * 60 * 1000).toISOString();
|
||||||
|
|
||||||
|
const CAPABILITY_ATTRIBUTES = [
|
||||||
|
"friendly_name",
|
||||||
|
"unit_of_measurement",
|
||||||
|
"icon",
|
||||||
|
"entity_picture",
|
||||||
|
"supported_features",
|
||||||
|
"hidden",
|
||||||
|
"assumed_state",
|
||||||
|
"device_class",
|
||||||
|
"state_class",
|
||||||
|
"restored",
|
||||||
|
];
|
||||||
export class Entity {
|
export class Entity {
|
||||||
public domain: string;
|
public domain: string;
|
||||||
|
|
||||||
@ -29,16 +41,28 @@ export class Entity {
|
|||||||
|
|
||||||
public hass?: any;
|
public hass?: any;
|
||||||
|
|
||||||
constructor(domain, objectId, state, baseAttributes) {
|
static CAPABILITY_ATTRIBUTES = new Set(CAPABILITY_ATTRIBUTES);
|
||||||
|
|
||||||
|
constructor(domain, objectId, state, attributes) {
|
||||||
this.domain = domain;
|
this.domain = domain;
|
||||||
this.objectId = objectId;
|
this.objectId = objectId;
|
||||||
this.entityId = `${domain}.${objectId}`;
|
this.entityId = `${domain}.${objectId}`;
|
||||||
this.lastChanged = randomTime();
|
this.lastChanged = randomTime();
|
||||||
this.lastUpdated = randomTime();
|
this.lastUpdated = randomTime();
|
||||||
this.state = String(state);
|
this.state = String(state);
|
||||||
|
|
||||||
// These are the attributes that we always write to the state machine
|
// These are the attributes that we always write to the state machine
|
||||||
|
const baseAttributes = {};
|
||||||
|
const capabilityAttributes =
|
||||||
|
TYPES[domain]?.CAPABILITY_ATTRIBUTES || Entity.CAPABILITY_ATTRIBUTES;
|
||||||
|
for (const key of Object.keys(attributes)) {
|
||||||
|
if (capabilityAttributes.has(key)) {
|
||||||
|
baseAttributes[key] = attributes[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.baseAttributes = baseAttributes;
|
this.baseAttributes = baseAttributes;
|
||||||
this.attributes = baseAttributes;
|
this.attributes = attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handleService(domain, service, data: Record<string, any>) {
|
public async handleService(domain, service, data: Record<string, any>) {
|
||||||
@ -54,7 +78,7 @@ export class Entity {
|
|||||||
this.lastUpdated = now();
|
this.lastUpdated = now();
|
||||||
this.lastChanged =
|
this.lastChanged =
|
||||||
state === this.state ? this.lastChanged : this.lastUpdated;
|
state === this.state ? this.lastChanged : this.lastUpdated;
|
||||||
this.attributes = { ...this.baseAttributes, ...attributes };
|
this.attributes = { ...this.attributes, ...attributes };
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
console.log("update", this.entityId, this);
|
console.log("update", this.entityId, this);
|
||||||
@ -68,7 +92,7 @@ export class Entity {
|
|||||||
return {
|
return {
|
||||||
entity_id: this.entityId,
|
entity_id: this.entityId,
|
||||||
state: this.state,
|
state: this.state,
|
||||||
attributes: this.attributes,
|
attributes: this.state === "off" ? this.baseAttributes : this.attributes,
|
||||||
last_changed: this.lastChanged,
|
last_changed: this.lastChanged,
|
||||||
last_updated: this.lastUpdated,
|
last_updated: this.lastUpdated,
|
||||||
};
|
};
|
||||||
@ -76,6 +100,16 @@ export class Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LightEntity extends Entity {
|
class LightEntity extends Entity {
|
||||||
|
static CAPABILITY_ATTRIBUTES = new Set([
|
||||||
|
...CAPABILITY_ATTRIBUTES,
|
||||||
|
"min_color_temp_kelvin",
|
||||||
|
"max_color_temp_kelvin",
|
||||||
|
"min_mireds",
|
||||||
|
"max_mireds",
|
||||||
|
"effect_list",
|
||||||
|
"supported_color_modes",
|
||||||
|
]);
|
||||||
|
|
||||||
public async handleService(domain, service, data) {
|
public async handleService(domain, service, data) {
|
||||||
if (!["homeassistant", this.domain].includes(domain)) {
|
if (!["homeassistant", this.domain].includes(domain)) {
|
||||||
return;
|
return;
|
||||||
@ -188,6 +222,12 @@ class AlarmControlPanelEntity extends Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MediaPlayerEntity extends Entity {
|
class MediaPlayerEntity extends Entity {
|
||||||
|
static CAPABILITY_ATTRIBUTES = new Set([
|
||||||
|
...CAPABILITY_ATTRIBUTES,
|
||||||
|
"source_list",
|
||||||
|
"sound_mode_list",
|
||||||
|
]);
|
||||||
|
|
||||||
public async handleService(
|
public async handleService(
|
||||||
domain,
|
domain,
|
||||||
service,
|
service,
|
||||||
@ -223,7 +263,11 @@ class CoverEntity extends Entity {
|
|||||||
if (service === "open_cover") {
|
if (service === "open_cover") {
|
||||||
this.update("open");
|
this.update("open");
|
||||||
} else if (service === "close_cover") {
|
} else if (service === "close_cover") {
|
||||||
this.update("closing");
|
this.update("closed");
|
||||||
|
} else if (service === "set_cover_position") {
|
||||||
|
this.update(data.position > 0 ? "open" : "closed", {
|
||||||
|
current_position: data.position,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
super.handleService(domain, service, data);
|
super.handleService(domain, service, data);
|
||||||
}
|
}
|
||||||
@ -288,6 +332,19 @@ class InputSelectEntity extends Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ClimateEntity extends Entity {
|
class ClimateEntity extends Entity {
|
||||||
|
static CAPABILITY_ATTRIBUTES = new Set([
|
||||||
|
...CAPABILITY_ATTRIBUTES,
|
||||||
|
"hvac_modes",
|
||||||
|
"min_temp",
|
||||||
|
"max_temp",
|
||||||
|
"target_temp_step",
|
||||||
|
"fan_modes",
|
||||||
|
"preset_modes",
|
||||||
|
"swing_modes",
|
||||||
|
"min_humidity",
|
||||||
|
"max_humidity",
|
||||||
|
]);
|
||||||
|
|
||||||
public async handleService(domain, service, data) {
|
public async handleService(domain, service, data) {
|
||||||
if (domain !== this.domain) {
|
if (domain !== this.domain) {
|
||||||
return;
|
return;
|
||||||
@ -357,6 +414,14 @@ class ClimateEntity extends Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class WaterHeaterEntity extends Entity {
|
class WaterHeaterEntity extends Entity {
|
||||||
|
static CAPABILITY_ATTRIBUTES = new Set([
|
||||||
|
...CAPABILITY_ATTRIBUTES,
|
||||||
|
"current_temperature",
|
||||||
|
"min_temp",
|
||||||
|
"max_temp",
|
||||||
|
"operation_list",
|
||||||
|
]);
|
||||||
|
|
||||||
public async handleService(domain, service, data) {
|
public async handleService(domain, service, data) {
|
||||||
if (domain !== this.domain) {
|
if (domain !== this.domain) {
|
||||||
return;
|
return;
|
||||||
|
@ -278,6 +278,8 @@ export const provideHass = (
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
async callService(domain, service, data) {
|
async callService(domain, service, data) {
|
||||||
if (data && "entity_id" in data) {
|
if (data && "entity_id" in data) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.log("Entity service call", domain, service, data);
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
ensureArray(data.entity_id).map((ent) =>
|
ensureArray(data.entity_id).map((ent) =>
|
||||||
entities[ent].handleService(domain, service, data)
|
entities[ent].handleService(domain, service, data)
|
||||||
|
@ -412,19 +412,19 @@ export class HuiAreaCard
|
|||||||
if (this._config.show_camera && "camera" in entitiesByDomain) {
|
if (this._config.show_camera && "camera" in entitiesByDomain) {
|
||||||
cameraEntityId = entitiesByDomain.camera[0].entity_id;
|
cameraEntityId = entitiesByDomain.camera[0].entity_id;
|
||||||
}
|
}
|
||||||
cameraEntityId = "camera.demo_camera";
|
|
||||||
|
|
||||||
const imageClass = area.picture || cameraEntityId;
|
const imageClass = area.picture || cameraEntityId;
|
||||||
|
|
||||||
const ignoreAspectRatio = imageClass || this.layout === "grid";
|
const ignoreAspectRatio = this.layout === "grid";
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card
|
<ha-card
|
||||||
class=${imageClass ? "image" : ""}
|
class=${imageClass ? "image" : ""}
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
paddingBottom: ignoreAspectRatio
|
paddingBottom:
|
||||||
? "0"
|
ignoreAspectRatio || imageClass
|
||||||
: `${((100 * this._ratio!.h) / this._ratio!.w).toFixed(2)}%`,
|
? "0"
|
||||||
|
: `${((100 * this._ratio!.h) / this._ratio!.w).toFixed(2)}%`,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
${area.picture || cameraEntityId
|
${area.picture || cameraEntityId
|
||||||
@ -435,8 +435,10 @@ export class HuiAreaCard
|
|||||||
.image=${area.picture ? area.picture : undefined}
|
.image=${area.picture ? area.picture : undefined}
|
||||||
.cameraImage=${cameraEntityId}
|
.cameraImage=${cameraEntityId}
|
||||||
.cameraView=${this._config.camera_view}
|
.cameraView=${this._config.camera_view}
|
||||||
.aspectRatio=${this._config.aspect_ratio ||
|
.aspectRatio=${ignoreAspectRatio
|
||||||
DEFAULT_ASPECT_RATIO}
|
? undefined
|
||||||
|
: this._config.aspect_ratio || DEFAULT_ASPECT_RATIO}
|
||||||
|
fitMode="cover"
|
||||||
></hui-image>
|
></hui-image>
|
||||||
`
|
`
|
||||||
: area.icon
|
: area.icon
|
||||||
@ -586,6 +588,10 @@ export class HuiAreaCard
|
|||||||
opacity: 0.12;
|
opacity: 0.12;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.image hui-image {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.icon-container {
|
.icon-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -40,11 +40,7 @@ import { findEntities } from "../common/find-entities";
|
|||||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||||
import "../components/hui-marquee";
|
import "../components/hui-marquee";
|
||||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||||
import type {
|
import type { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||||
LovelaceCard,
|
|
||||||
LovelaceCardEditor,
|
|
||||||
LovelaceLayoutOptions,
|
|
||||||
} from "../types";
|
|
||||||
import { MediaControlCardConfig } from "./types";
|
import { MediaControlCardConfig } from "./types";
|
||||||
|
|
||||||
@customElement("hui-media-control-card")
|
@customElement("hui-media-control-card")
|
||||||
@ -586,15 +582,6 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getLayoutOptions(): LovelaceLayoutOptions {
|
|
||||||
return {
|
|
||||||
grid_columns: 4,
|
|
||||||
grid_min_columns: 2,
|
|
||||||
grid_rows: 3,
|
|
||||||
grid_min_rows: 3,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
ha-card {
|
ha-card {
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
// https://github.com/home-assistant/frontend/pull/7031
|
|
||||||
|
|
||||||
export {}; // for Babel to treat as a module
|
|
||||||
|
|
||||||
const isSafari14 = /^((?!chrome|android).)*version\/14\.0\s.*safari/i.test(
|
|
||||||
navigator.userAgent
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isSafari14) {
|
|
||||||
const origAttachShadow = window.Element.prototype.attachShadow;
|
|
||||||
window.Element.prototype.attachShadow = function (init) {
|
|
||||||
if (init && init.delegatesFocus) {
|
|
||||||
delete init.delegatesFocus;
|
|
||||||
}
|
|
||||||
return origAttachShadow.apply(this, [init]);
|
|
||||||
};
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user