mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Add user condition to conditional card (#18265)
* Add user condition to conditional card * Refactor user fetch * Add validate ui * Use ha-check-list-item
This commit is contained in:
parent
4354ad3807
commit
eedb42b2f3
@ -1,7 +1,8 @@
|
|||||||
import { mdiResponsive, mdiStateMachine } from "@mdi/js";
|
import { mdiAccount, mdiResponsive, mdiStateMachine } from "@mdi/js";
|
||||||
import { Condition } from "./validate-condition";
|
import { Condition } from "./validate-condition";
|
||||||
|
|
||||||
export const ICON_CONDITION: Record<Condition["condition"], string> = {
|
export const ICON_CONDITION: Record<Condition["condition"], string> = {
|
||||||
state: mdiStateMachine,
|
state: mdiStateMachine,
|
||||||
screen: mdiResponsive,
|
screen: mdiResponsive,
|
||||||
|
user: mdiAccount,
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { UNAVAILABLE } from "../../../data/entity";
|
import { UNAVAILABLE } from "../../../data/entity";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
export type Condition = StateCondition | ScreenCondition;
|
export type Condition = StateCondition | ScreenCondition | UserCondition;
|
||||||
|
|
||||||
export type LegacyCondition = {
|
export type LegacyCondition = {
|
||||||
entity?: string;
|
entity?: string;
|
||||||
@ -21,6 +21,11 @@ export type ScreenCondition = {
|
|||||||
media_query?: string;
|
media_query?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UserCondition = {
|
||||||
|
condition: "user";
|
||||||
|
users?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
function checkStateCondition(
|
function checkStateCondition(
|
||||||
condition: StateCondition | LegacyCondition,
|
condition: StateCondition | LegacyCondition,
|
||||||
hass: HomeAssistant
|
hass: HomeAssistant
|
||||||
@ -35,30 +40,38 @@ function checkStateCondition(
|
|||||||
: state !== condition.state_not;
|
: state !== condition.state_not;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkScreenCondition(
|
function checkScreenCondition(condition: ScreenCondition, _: HomeAssistant) {
|
||||||
condition: ScreenCondition,
|
|
||||||
_hass: HomeAssistant
|
|
||||||
) {
|
|
||||||
return condition.media_query
|
return condition.media_query
|
||||||
? matchMedia(condition.media_query).matches
|
? matchMedia(condition.media_query).matches
|
||||||
: false;
|
: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkUserCondition(condition: UserCondition, hass: HomeAssistant) {
|
||||||
|
return condition.users && hass.user?.id
|
||||||
|
? condition.users.includes(hass.user.id)
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
export function checkConditionsMet(
|
export function checkConditionsMet(
|
||||||
conditions: (Condition | LegacyCondition)[],
|
conditions: (Condition | LegacyCondition)[],
|
||||||
hass: HomeAssistant
|
hass: HomeAssistant
|
||||||
): boolean {
|
): boolean {
|
||||||
return conditions.every((c) => {
|
return conditions.every((c) => {
|
||||||
if ("condition" in c) {
|
if ("condition" in c) {
|
||||||
if (c.condition === "screen") {
|
switch (c.condition) {
|
||||||
return checkScreenCondition(c, hass);
|
case "screen":
|
||||||
|
return checkScreenCondition(c, hass);
|
||||||
|
case "user":
|
||||||
|
return checkUserCondition(c, hass);
|
||||||
|
default:
|
||||||
|
return checkStateCondition(c, hass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return checkStateCondition(c, hass);
|
return checkStateCondition(c, hass);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function valideStateCondition(condition: StateCondition | LegacyCondition) {
|
function validateStateCondition(condition: StateCondition | LegacyCondition) {
|
||||||
return (
|
return (
|
||||||
condition.entity != null &&
|
condition.entity != null &&
|
||||||
(condition.state != null || condition.state_not != null)
|
(condition.state != null || condition.state_not != null)
|
||||||
@ -69,15 +82,24 @@ function validateScreenCondition(condition: ScreenCondition) {
|
|||||||
return condition.media_query != null;
|
return condition.media_query != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateUserCondition(condition: UserCondition) {
|
||||||
|
return condition.users != null;
|
||||||
|
}
|
||||||
|
|
||||||
export function validateConditionalConfig(
|
export function validateConditionalConfig(
|
||||||
conditions: (Condition | LegacyCondition)[]
|
conditions: (Condition | LegacyCondition)[]
|
||||||
): boolean {
|
): boolean {
|
||||||
return conditions.every((c) => {
|
return conditions.every((c) => {
|
||||||
if ("condition" in c) {
|
if ("condition" in c) {
|
||||||
if (c.condition === "screen") {
|
switch (c.condition) {
|
||||||
return validateScreenCondition(c);
|
case "screen":
|
||||||
|
return validateScreenCondition(c);
|
||||||
|
case "user":
|
||||||
|
return validateUserCondition(c);
|
||||||
|
default:
|
||||||
|
return validateStateCondition(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return valideStateCondition(c);
|
return validateStateCondition(c);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,12 @@ import "./ha-card-condition-editor";
|
|||||||
import { LovelaceConditionEditorConstructor } from "./types";
|
import { LovelaceConditionEditorConstructor } from "./types";
|
||||||
import "./types/ha-card-condition-screen";
|
import "./types/ha-card-condition-screen";
|
||||||
import "./types/ha-card-condition-state";
|
import "./types/ha-card-condition-state";
|
||||||
|
import "./types/ha-card-condition-user";
|
||||||
|
|
||||||
const UI_CONDITION = [
|
const UI_CONDITION = [
|
||||||
"state",
|
"state",
|
||||||
"screen",
|
"screen",
|
||||||
|
"user",
|
||||||
] as const satisfies readonly Condition["condition"][];
|
] as const satisfies readonly Condition["condition"][];
|
||||||
|
|
||||||
@customElement("ha-card-conditions-editor")
|
@customElement("ha-card-conditions-editor")
|
||||||
|
@ -0,0 +1,122 @@
|
|||||||
|
import "@material/mwc-list";
|
||||||
|
import { LitElement, PropertyValues, css, html } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { array, assert, literal, object, string } from "superstruct";
|
||||||
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
import { stringCompare } from "../../../../../common/string/compare";
|
||||||
|
import "../../../../../components/ha-check-list-item";
|
||||||
|
import "../../../../../components/ha-switch";
|
||||||
|
import "../../../../../components/user/ha-user-badge";
|
||||||
|
import { User, fetchUsers } from "../../../../../data/user";
|
||||||
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
|
import { UserCondition } from "../../../common/validate-condition";
|
||||||
|
|
||||||
|
const userConditionStruct = object({
|
||||||
|
condition: literal("user"),
|
||||||
|
users: array(string()),
|
||||||
|
});
|
||||||
|
|
||||||
|
@customElement("ha-card-condition-user")
|
||||||
|
export class HaCardConditionUser extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public condition!: UserCondition;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
public static get defaultConfig(): UserCondition {
|
||||||
|
return { condition: "user", users: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
@state() private _users: User[] = [];
|
||||||
|
|
||||||
|
protected static validateUIConfig(condition: UserCondition) {
|
||||||
|
return assert(condition, userConditionStruct);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _sortedUsers = memoizeOne((users: User[]) =>
|
||||||
|
users.sort((a, b) =>
|
||||||
|
stringCompare(a.name, b.name, this.hass.locale.language)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
protected async firstUpdated(changedProps: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
this._fetchUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _fetchUsers() {
|
||||||
|
const users = await fetchUsers(this.hass);
|
||||||
|
this._users = users.filter((user) => !user.system_generated);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const selectedUsers = this.condition.users ?? [];
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<mwc-list>
|
||||||
|
${this._sortedUsers(this._users).map(
|
||||||
|
(user) => html`
|
||||||
|
<ha-check-list-item
|
||||||
|
graphic="avatar"
|
||||||
|
hasMeta
|
||||||
|
.userId=${user.id}
|
||||||
|
.selected=${selectedUsers.includes(user.id)}
|
||||||
|
@request-selected=${this._userChanged}
|
||||||
|
>
|
||||||
|
<ha-user-badge
|
||||||
|
slot="graphic"
|
||||||
|
.hass=${this.hass}
|
||||||
|
.user=${user}
|
||||||
|
></ha-user-badge>
|
||||||
|
<span>${user.name}</span>
|
||||||
|
</ha-check-list-item>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</mwc-list>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _userChanged(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const selectedUsers = this.condition.users ?? [];
|
||||||
|
const userId = ev.currentTarget.userId as string;
|
||||||
|
const checked = ev.detail.selected as boolean;
|
||||||
|
|
||||||
|
if (checked === selectedUsers.includes(userId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let users = selectedUsers;
|
||||||
|
if (checked) {
|
||||||
|
users = [...users, userId];
|
||||||
|
} else {
|
||||||
|
users = users.filter((user) => user !== userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const condition: UserCondition = {
|
||||||
|
...this.condition,
|
||||||
|
users,
|
||||||
|
};
|
||||||
|
|
||||||
|
fireEvent(this, "value-changed", { value: condition });
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
mwc-list {
|
||||||
|
--mdc-list-vertical-padding: 0;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-card-condition-user": HaCardConditionUser;
|
||||||
|
}
|
||||||
|
}
|
@ -4774,6 +4774,9 @@
|
|||||||
"state_equal": "State is equal to",
|
"state_equal": "State is equal to",
|
||||||
"state_not_equal": "State is not equal to",
|
"state_not_equal": "State is not equal to",
|
||||||
"current_state": "current"
|
"current_state": "current"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"label": "User"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user