mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 11:46:42 +00:00
Logbook: Localize Message & Style updates (#6978)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
26162815c8
commit
e2427c8dce
@ -7,6 +7,66 @@
|
|||||||
/** Icon to use when no icon specified for domain. */
|
/** Icon to use when no icon specified for domain. */
|
||||||
export const DEFAULT_DOMAIN_ICON = "hass:bookmark";
|
export const DEFAULT_DOMAIN_ICON = "hass:bookmark";
|
||||||
|
|
||||||
|
/** Icons for each domain */
|
||||||
|
export const FIXED_DOMAIN_ICONS = {
|
||||||
|
alert: "hass:alert",
|
||||||
|
alexa: "hass:amazon-alexa",
|
||||||
|
air_quality: "hass:air-filter",
|
||||||
|
automation: "hass:robot",
|
||||||
|
calendar: "hass:calendar",
|
||||||
|
camera: "hass:video",
|
||||||
|
climate: "hass:thermostat",
|
||||||
|
configurator: "hass:cog",
|
||||||
|
conversation: "hass:text-to-speech",
|
||||||
|
counter: "hass:counter",
|
||||||
|
device_tracker: "hass:account",
|
||||||
|
fan: "hass:fan",
|
||||||
|
google_assistant: "hass:google-assistant",
|
||||||
|
group: "hass:google-circles-communities",
|
||||||
|
homeassistant: "hass:home-assistant",
|
||||||
|
homekit: "hass:home-automation",
|
||||||
|
image_processing: "hass:image-filter-frames",
|
||||||
|
input_boolean: "hass:toggle-switch-outline",
|
||||||
|
input_datetime: "hass:calendar-clock",
|
||||||
|
input_number: "hass:ray-vertex",
|
||||||
|
input_select: "hass:format-list-bulleted",
|
||||||
|
input_text: "hass:form-textbox",
|
||||||
|
light: "hass:lightbulb",
|
||||||
|
mailbox: "hass:mailbox",
|
||||||
|
notify: "hass:comment-alert",
|
||||||
|
persistent_notification: "hass:bell",
|
||||||
|
person: "hass:account",
|
||||||
|
plant: "hass:flower",
|
||||||
|
proximity: "hass:apple-safari",
|
||||||
|
remote: "hass:remote",
|
||||||
|
scene: "hass:palette",
|
||||||
|
script: "hass:script-text",
|
||||||
|
sensor: "hass:eye",
|
||||||
|
simple_alarm: "hass:bell",
|
||||||
|
sun: "hass:white-balance-sunny",
|
||||||
|
switch: "hass:flash",
|
||||||
|
timer: "hass:timer-outline",
|
||||||
|
updater: "hass:cloud-upload",
|
||||||
|
vacuum: "hass:robot-vacuum",
|
||||||
|
water_heater: "hass:thermometer",
|
||||||
|
weather: "hass:weather-cloudy",
|
||||||
|
zone: "hass:map-marker-radius",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FIXED_DEVICE_CLASS_ICONS = {
|
||||||
|
current: "hass:current-ac",
|
||||||
|
energy: "hass:flash",
|
||||||
|
humidity: "hass:water-percent",
|
||||||
|
illuminance: "hass:brightness-5",
|
||||||
|
temperature: "hass:thermometer",
|
||||||
|
pressure: "hass:gauge",
|
||||||
|
power: "hass:flash",
|
||||||
|
power_factor: "hass:angle-acute",
|
||||||
|
signal_strength: "hass:wifi",
|
||||||
|
timestamp: "hass:clock",
|
||||||
|
voltage: "hass:sine-wave",
|
||||||
|
};
|
||||||
|
|
||||||
/** Domains that have a state card. */
|
/** Domains that have a state card. */
|
||||||
export const DOMAINS_WITH_CARD = [
|
export const DOMAINS_WITH_CARD = [
|
||||||
"climate",
|
"climate",
|
||||||
@ -63,6 +123,10 @@ export const DOMAINS_MORE_INFO_NO_HISTORY = ["camera", "configurator", "scene"];
|
|||||||
/** States that we consider "off". */
|
/** States that we consider "off". */
|
||||||
export const STATES_OFF = ["closed", "locked", "off"];
|
export const STATES_OFF = ["closed", "locked", "off"];
|
||||||
|
|
||||||
|
/** Binary States */
|
||||||
|
export const BINARY_STATE_ON = "on";
|
||||||
|
export const BINARY_STATE_OFF = "off";
|
||||||
|
|
||||||
/** Domains where we allow toggle in Lovelace. */
|
/** Domains where we allow toggle in Lovelace. */
|
||||||
export const DOMAINS_TOGGLE = new Set([
|
export const DOMAINS_TOGGLE = new Set([
|
||||||
"fan",
|
"fan",
|
||||||
|
@ -2,9 +2,9 @@ import { HassEntity } from "home-assistant-js-websocket";
|
|||||||
|
|
||||||
/** Return an icon representing a binary sensor state. */
|
/** Return an icon representing a binary sensor state. */
|
||||||
|
|
||||||
export const binarySensorIcon = (state: HassEntity) => {
|
export const binarySensorIcon = (state?: string, stateObj?: HassEntity) => {
|
||||||
const is_off = state.state && state.state === "off";
|
const is_off = state === "off";
|
||||||
switch (state.attributes.device_class) {
|
switch (stateObj?.attributes.device_class) {
|
||||||
case "battery":
|
case "battery":
|
||||||
return is_off ? "hass:battery" : "hass:battery-outline";
|
return is_off ? "hass:battery" : "hass:battery-outline";
|
||||||
case "battery_charging":
|
case "battery_charging":
|
||||||
|
@ -9,14 +9,17 @@ import { computeStateDomain } from "./compute_state_domain";
|
|||||||
export const computeStateDisplay = (
|
export const computeStateDisplay = (
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
stateObj: HassEntity,
|
stateObj: HassEntity,
|
||||||
language: string
|
language: string,
|
||||||
|
state?: string
|
||||||
): string => {
|
): string => {
|
||||||
if (stateObj.state === UNKNOWN || stateObj.state === UNAVAILABLE) {
|
const compareState = state !== undefined ? state : stateObj.state;
|
||||||
return localize(`state.default.${stateObj.state}`);
|
|
||||||
|
if (compareState === UNKNOWN || compareState === UNAVAILABLE) {
|
||||||
|
return localize(`state.default.${compareState}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stateObj.attributes.unit_of_measurement) {
|
if (stateObj.attributes.unit_of_measurement) {
|
||||||
return `${stateObj.state} ${stateObj.attributes.unit_of_measurement}`;
|
return `${compareState} ${stateObj.attributes.unit_of_measurement}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const domain = computeStateDomain(stateObj);
|
const domain = computeStateDomain(stateObj);
|
||||||
@ -56,7 +59,7 @@ export const computeStateDisplay = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (domain === "humidifier") {
|
if (domain === "humidifier") {
|
||||||
if (stateObj.state === "on" && stateObj.attributes.humidity) {
|
if (compareState === "on" && stateObj.attributes.humidity) {
|
||||||
return `${stateObj.attributes.humidity}%`;
|
return `${stateObj.attributes.humidity}%`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,11 +68,11 @@ export const computeStateDisplay = (
|
|||||||
// Return device class translation
|
// Return device class translation
|
||||||
(stateObj.attributes.device_class &&
|
(stateObj.attributes.device_class &&
|
||||||
localize(
|
localize(
|
||||||
`component.${domain}.state.${stateObj.attributes.device_class}.${stateObj.state}`
|
`component.${domain}.state.${stateObj.attributes.device_class}.${compareState}`
|
||||||
)) ||
|
)) ||
|
||||||
// Return default translation
|
// Return default translation
|
||||||
localize(`component.${domain}.state._.${stateObj.state}`) ||
|
localize(`component.${domain}.state._.${compareState}`) ||
|
||||||
// We don't know! Return the raw state.
|
// We don't know! Return the raw state.
|
||||||
stateObj.state
|
compareState
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
/** Return an icon representing a cover state. */
|
/** Return an icon representing a cover state. */
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { domainIcon } from "./domain_icon";
|
|
||||||
|
|
||||||
export const coverIcon = (state: HassEntity): string => {
|
export const coverIcon = (state?: string, stateObj?: HassEntity): string => {
|
||||||
const open = state.state !== "closed";
|
const open = state !== "closed";
|
||||||
|
|
||||||
switch (state.attributes.device_class) {
|
switch (stateObj?.attributes.device_class) {
|
||||||
case "garage":
|
case "garage":
|
||||||
switch (state.state) {
|
switch (state) {
|
||||||
case "opening":
|
case "opening":
|
||||||
return "hass:arrow-up-box";
|
return "hass:arrow-up-box";
|
||||||
case "closing":
|
case "closing":
|
||||||
@ -18,7 +17,7 @@ export const coverIcon = (state: HassEntity): string => {
|
|||||||
return "hass:garage-open";
|
return "hass:garage-open";
|
||||||
}
|
}
|
||||||
case "gate":
|
case "gate":
|
||||||
switch (state.state) {
|
switch (state) {
|
||||||
case "opening":
|
case "opening":
|
||||||
case "closing":
|
case "closing":
|
||||||
return "hass:gate-arrow-right";
|
return "hass:gate-arrow-right";
|
||||||
@ -32,7 +31,7 @@ export const coverIcon = (state: HassEntity): string => {
|
|||||||
case "damper":
|
case "damper":
|
||||||
return open ? "hass:circle" : "hass:circle-slice-8";
|
return open ? "hass:circle" : "hass:circle-slice-8";
|
||||||
case "shutter":
|
case "shutter":
|
||||||
switch (state.state) {
|
switch (state) {
|
||||||
case "opening":
|
case "opening":
|
||||||
return "hass:arrow-up-box";
|
return "hass:arrow-up-box";
|
||||||
case "closing":
|
case "closing":
|
||||||
@ -44,7 +43,7 @@ export const coverIcon = (state: HassEntity): string => {
|
|||||||
}
|
}
|
||||||
case "blind":
|
case "blind":
|
||||||
case "curtain":
|
case "curtain":
|
||||||
switch (state.state) {
|
switch (state) {
|
||||||
case "opening":
|
case "opening":
|
||||||
return "hass:arrow-up-box";
|
return "hass:arrow-up-box";
|
||||||
case "closing":
|
case "closing":
|
||||||
@ -55,7 +54,7 @@ export const coverIcon = (state: HassEntity): string => {
|
|||||||
return "hass:blinds-open";
|
return "hass:blinds-open";
|
||||||
}
|
}
|
||||||
case "window":
|
case "window":
|
||||||
switch (state.state) {
|
switch (state) {
|
||||||
case "opening":
|
case "opening":
|
||||||
return "hass:arrow-up-box";
|
return "hass:arrow-up-box";
|
||||||
case "closing":
|
case "closing":
|
||||||
@ -65,7 +64,16 @@ export const coverIcon = (state: HassEntity): string => {
|
|||||||
default:
|
default:
|
||||||
return "hass:window-open";
|
return "hass:window-open";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case "opening":
|
||||||
|
return "hass:arrow-up-box";
|
||||||
|
case "closing":
|
||||||
|
return "hass:arrow-down-box";
|
||||||
|
case "closed":
|
||||||
|
return "hass:window-closed";
|
||||||
default:
|
default:
|
||||||
return domainIcon("cover", state.state);
|
return "hass:window-open";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,63 +1,24 @@
|
|||||||
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
/**
|
/**
|
||||||
* Return the icon to be used for a domain.
|
* Return the icon to be used for a domain.
|
||||||
*
|
*
|
||||||
* Optionally pass in a state to influence the domain icon.
|
* Optionally pass in a state to influence the domain icon.
|
||||||
*/
|
*/
|
||||||
import { DEFAULT_DOMAIN_ICON } from "../const";
|
import { DEFAULT_DOMAIN_ICON, FIXED_DOMAIN_ICONS } from "../const";
|
||||||
|
import { binarySensorIcon } from "./binary_sensor_icon";
|
||||||
|
import { coverIcon } from "./cover_icon";
|
||||||
|
import { sensorIcon } from "./sensor_icon";
|
||||||
|
|
||||||
const fixedIcons = {
|
export const domainIcon = (
|
||||||
alert: "hass:alert",
|
domain: string,
|
||||||
alexa: "hass:amazon-alexa",
|
stateObj?: HassEntity,
|
||||||
air_quality: "hass:air-filter",
|
state?: string
|
||||||
automation: "hass:robot",
|
): string => {
|
||||||
calendar: "hass:calendar",
|
const compareState = state !== undefined ? state : stateObj?.state;
|
||||||
camera: "hass:video",
|
|
||||||
climate: "hass:thermostat",
|
|
||||||
configurator: "hass:cog",
|
|
||||||
conversation: "hass:text-to-speech",
|
|
||||||
counter: "hass:counter",
|
|
||||||
device_tracker: "hass:account",
|
|
||||||
fan: "hass:fan",
|
|
||||||
google_assistant: "hass:google-assistant",
|
|
||||||
group: "hass:google-circles-communities",
|
|
||||||
homeassistant: "hass:home-assistant",
|
|
||||||
homekit: "hass:home-automation",
|
|
||||||
image_processing: "hass:image-filter-frames",
|
|
||||||
input_boolean: "hass:toggle-switch-outline",
|
|
||||||
input_datetime: "hass:calendar-clock",
|
|
||||||
input_number: "hass:ray-vertex",
|
|
||||||
input_select: "hass:format-list-bulleted",
|
|
||||||
input_text: "hass:form-textbox",
|
|
||||||
light: "hass:lightbulb",
|
|
||||||
mailbox: "hass:mailbox",
|
|
||||||
notify: "hass:comment-alert",
|
|
||||||
persistent_notification: "hass:bell",
|
|
||||||
person: "hass:account",
|
|
||||||
plant: "hass:flower",
|
|
||||||
proximity: "hass:apple-safari",
|
|
||||||
remote: "hass:remote",
|
|
||||||
scene: "hass:palette",
|
|
||||||
script: "hass:script-text",
|
|
||||||
sensor: "hass:eye",
|
|
||||||
simple_alarm: "hass:bell",
|
|
||||||
sun: "hass:white-balance-sunny",
|
|
||||||
switch: "hass:flash",
|
|
||||||
timer: "hass:timer-outline",
|
|
||||||
updater: "hass:cloud-upload",
|
|
||||||
vacuum: "hass:robot-vacuum",
|
|
||||||
water_heater: "hass:thermometer",
|
|
||||||
weather: "hass:weather-cloudy",
|
|
||||||
zone: "hass:map-marker-radius",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const domainIcon = (domain: string, state?: string): string => {
|
|
||||||
if (domain in fixedIcons) {
|
|
||||||
return fixedIcons[domain];
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (domain) {
|
switch (domain) {
|
||||||
case "alarm_control_panel":
|
case "alarm_control_panel":
|
||||||
switch (state) {
|
switch (compareState) {
|
||||||
case "armed_home":
|
case "armed_home":
|
||||||
return "hass:bell-plus";
|
return "hass:bell-plus";
|
||||||
case "armed_night":
|
case "armed_night":
|
||||||
@ -71,21 +32,10 @@ export const domainIcon = (domain: string, state?: string): string => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "binary_sensor":
|
case "binary_sensor":
|
||||||
return state && state === "off"
|
return binarySensorIcon(compareState, stateObj);
|
||||||
? "hass:radiobox-blank"
|
|
||||||
: "hass:checkbox-marked-circle";
|
|
||||||
|
|
||||||
case "cover":
|
case "cover":
|
||||||
switch (state) {
|
return coverIcon(compareState, stateObj);
|
||||||
case "opening":
|
|
||||||
return "hass:arrow-up-box";
|
|
||||||
case "closing":
|
|
||||||
return "hass:arrow-down-box";
|
|
||||||
case "closed":
|
|
||||||
return "hass:window-closed";
|
|
||||||
default:
|
|
||||||
return "hass:window-open";
|
|
||||||
}
|
|
||||||
|
|
||||||
case "humidifier":
|
case "humidifier":
|
||||||
return state && state === "off"
|
return state && state === "off"
|
||||||
@ -93,13 +43,13 @@ export const domainIcon = (domain: string, state?: string): string => {
|
|||||||
: "hass:air-humidifier";
|
: "hass:air-humidifier";
|
||||||
|
|
||||||
case "lock":
|
case "lock":
|
||||||
return state && state === "unlocked" ? "hass:lock-open" : "hass:lock";
|
return compareState === "unlocked" ? "hass:lock-open" : "hass:lock";
|
||||||
|
|
||||||
case "media_player":
|
case "media_player":
|
||||||
return state && state === "playing" ? "hass:cast-connected" : "hass:cast";
|
return compareState === "playing" ? "hass:cast-connected" : "hass:cast";
|
||||||
|
|
||||||
case "zwave":
|
case "zwave":
|
||||||
switch (state) {
|
switch (compareState) {
|
||||||
case "dead":
|
case "dead":
|
||||||
return "hass:emoticon-dead";
|
return "hass:emoticon-dead";
|
||||||
case "sleeping":
|
case "sleeping":
|
||||||
@ -110,11 +60,32 @@ export const domainIcon = (domain: string, state?: string): string => {
|
|||||||
return "hass:z-wave";
|
return "hass:z-wave";
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
case "sensor": {
|
||||||
// eslint-disable-next-line
|
const icon = sensorIcon(stateObj);
|
||||||
console.warn(
|
if (icon) {
|
||||||
"Unable to find icon for domain " + domain + " (" + state + ")"
|
return icon;
|
||||||
);
|
}
|
||||||
return DEFAULT_DOMAIN_ICON;
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "input_datetime":
|
||||||
|
if (!stateObj?.attributes.has_date) {
|
||||||
|
return "hass:clock";
|
||||||
|
}
|
||||||
|
if (!stateObj.attributes.has_time) {
|
||||||
|
return "hass:calendar";
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (domain in FIXED_DOMAIN_ICONS) {
|
||||||
|
return FIXED_DOMAIN_ICONS[domain];
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.warn(
|
||||||
|
"Unable to find icon for domain " + domain + " (" + stateObj + ")"
|
||||||
|
);
|
||||||
|
return DEFAULT_DOMAIN_ICON;
|
||||||
};
|
};
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
/** Return an icon representing an input datetime state. */
|
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
|
||||||
import { domainIcon } from "./domain_icon";
|
|
||||||
|
|
||||||
export const inputDateTimeIcon = (state: HassEntity): string => {
|
|
||||||
if (!state.attributes.has_date) {
|
|
||||||
return "hass:clock";
|
|
||||||
}
|
|
||||||
if (!state.attributes.has_time) {
|
|
||||||
return "hass:calendar";
|
|
||||||
}
|
|
||||||
return domainIcon("input_datetime");
|
|
||||||
};
|
|
@ -1,36 +1,23 @@
|
|||||||
/** Return an icon representing a sensor state. */
|
/** Return an icon representing a sensor state. */
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { UNIT_C, UNIT_F } from "../const";
|
import { FIXED_DEVICE_CLASS_ICONS, UNIT_C, UNIT_F } from "../const";
|
||||||
import { domainIcon } from "./domain_icon";
|
|
||||||
import { batteryIcon } from "./battery_icon";
|
import { batteryIcon } from "./battery_icon";
|
||||||
|
|
||||||
const fixedDeviceClassIcons = {
|
export const sensorIcon = (stateObj?: HassEntity): string | undefined => {
|
||||||
current: "hass:current-ac",
|
const dclass = stateObj?.attributes.device_class;
|
||||||
energy: "hass:flash",
|
|
||||||
humidity: "hass:water-percent",
|
|
||||||
illuminance: "hass:brightness-5",
|
|
||||||
temperature: "hass:thermometer",
|
|
||||||
pressure: "hass:gauge",
|
|
||||||
power: "hass:flash",
|
|
||||||
power_factor: "hass:angle-acute",
|
|
||||||
signal_strength: "hass:wifi",
|
|
||||||
timestamp: "hass:clock",
|
|
||||||
voltage: "hass:sine-wave",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const sensorIcon = (state: HassEntity) => {
|
if (dclass && dclass in FIXED_DEVICE_CLASS_ICONS) {
|
||||||
const dclass = state.attributes.device_class;
|
return FIXED_DEVICE_CLASS_ICONS[dclass];
|
||||||
|
|
||||||
if (dclass && dclass in fixedDeviceClassIcons) {
|
|
||||||
return fixedDeviceClassIcons[dclass];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dclass === "battery") {
|
if (dclass === "battery") {
|
||||||
return batteryIcon(state);
|
return stateObj ? batteryIcon(stateObj) : "hass:battery";
|
||||||
}
|
}
|
||||||
|
|
||||||
const unit = state.attributes.unit_of_measurement;
|
const unit = stateObj?.attributes.unit_of_measurement;
|
||||||
if (unit === UNIT_C || unit === UNIT_F) {
|
if (unit === UNIT_C || unit === UNIT_F) {
|
||||||
return "hass:thermometer";
|
return "hass:thermometer";
|
||||||
}
|
}
|
||||||
return domainIcon("sensor");
|
|
||||||
|
return undefined;
|
||||||
};
|
};
|
||||||
|
@ -1,19 +1,8 @@
|
|||||||
/** Return an icon representing a state. */
|
/** Return an icon representing a state. */
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { DEFAULT_DOMAIN_ICON } from "../const";
|
import { DEFAULT_DOMAIN_ICON } from "../const";
|
||||||
import { binarySensorIcon } from "./binary_sensor_icon";
|
|
||||||
import { computeDomain } from "./compute_domain";
|
import { computeDomain } from "./compute_domain";
|
||||||
import { coverIcon } from "./cover_icon";
|
|
||||||
import { domainIcon } from "./domain_icon";
|
import { domainIcon } from "./domain_icon";
|
||||||
import { inputDateTimeIcon } from "./input_dateteime_icon";
|
|
||||||
import { sensorIcon } from "./sensor_icon";
|
|
||||||
|
|
||||||
const domainIcons = {
|
|
||||||
binary_sensor: binarySensorIcon,
|
|
||||||
cover: coverIcon,
|
|
||||||
sensor: sensorIcon,
|
|
||||||
input_datetime: inputDateTimeIcon,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const stateIcon = (state: HassEntity) => {
|
export const stateIcon = (state: HassEntity) => {
|
||||||
if (!state) {
|
if (!state) {
|
||||||
@ -23,10 +12,5 @@ export const stateIcon = (state: HassEntity) => {
|
|||||||
return state.attributes.icon;
|
return state.attributes.icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
const domain = computeDomain(state.entity_id);
|
return domainIcon(computeDomain(state.entity_id), state);
|
||||||
|
|
||||||
if (domain in domainIcons) {
|
|
||||||
return domainIcons[domain](state);
|
|
||||||
}
|
|
||||||
return domainIcon(domain, state.state);
|
|
||||||
};
|
};
|
||||||
|
@ -147,7 +147,7 @@ export class HaStateLabelBadge extends LitElement {
|
|||||||
return "hass:alert-circle";
|
return "hass:alert-circle";
|
||||||
}
|
}
|
||||||
// state == 'disarmed'
|
// state == 'disarmed'
|
||||||
return domainIcon(domain, state.state);
|
return domainIcon(domain, state);
|
||||||
case "binary_sensor":
|
case "binary_sensor":
|
||||||
case "device_tracker":
|
case "device_tracker":
|
||||||
case "updater":
|
case "updater":
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { BINARY_STATE_OFF, BINARY_STATE_ON } from "../common/const";
|
||||||
|
import { computeDomain } from "../common/entity/compute_domain";
|
||||||
|
import { computeStateDisplay } from "../common/entity/compute_state_display";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
import { UNAVAILABLE_STATES } from "./entity";
|
||||||
|
|
||||||
|
const LOGBOOK_LOCALIZE_PATH = "ui.components.logbook.messages";
|
||||||
|
|
||||||
export interface LogbookEntry {
|
export interface LogbookEntry {
|
||||||
when: string;
|
when: string;
|
||||||
name: string;
|
name: string;
|
||||||
message: string;
|
message?: string;
|
||||||
entity_id?: string;
|
entity_id?: string;
|
||||||
domain: string;
|
domain: string;
|
||||||
context_user_id?: string;
|
context_user_id?: string;
|
||||||
@ -13,13 +20,43 @@ export interface LogbookEntry {
|
|||||||
context_entity_id?: string;
|
context_entity_id?: string;
|
||||||
context_entity_id_name?: string;
|
context_entity_id_name?: string;
|
||||||
context_name?: string;
|
context_name?: string;
|
||||||
|
state?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DATA_CACHE: {
|
const DATA_CACHE: {
|
||||||
[cacheKey: string]: { [entityId: string]: Promise<LogbookEntry[]> };
|
[cacheKey: string]: { [entityId: string]: Promise<LogbookEntry[]> };
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
export const getLogbookData = (
|
export const getLogbookData = async (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
startDate: string,
|
||||||
|
endDate: string,
|
||||||
|
entityId?: string,
|
||||||
|
entity_matches_only?: boolean
|
||||||
|
) => {
|
||||||
|
const logbookData = await getLogbookDataCache(
|
||||||
|
hass,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
entityId,
|
||||||
|
entity_matches_only
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const entry of logbookData) {
|
||||||
|
if (entry.state) {
|
||||||
|
entry.message = getLogbookMessage(
|
||||||
|
hass,
|
||||||
|
entry.state,
|
||||||
|
hass!.states[entry.entity_id!],
|
||||||
|
computeDomain(entry.entity_id!)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return logbookData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLogbookDataCache = async (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
startDate: string,
|
startDate: string,
|
||||||
endDate: string,
|
endDate: string,
|
||||||
@ -43,9 +80,8 @@ export const getLogbookData = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (entityId !== ALL_ENTITIES && DATA_CACHE[cacheKey][ALL_ENTITIES]) {
|
if (entityId !== ALL_ENTITIES && DATA_CACHE[cacheKey][ALL_ENTITIES]) {
|
||||||
return DATA_CACHE[cacheKey][ALL_ENTITIES].then((entities) =>
|
const entities = await DATA_CACHE[cacheKey][ALL_ENTITIES];
|
||||||
entities.filter((entity) => entity.entity_id === entityId)
|
return entities.filter((entity) => entity.entity_id === entityId);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DATA_CACHE[cacheKey][entityId] = getLogbookDataFromServer(
|
DATA_CACHE[cacheKey][entityId] = getLogbookDataFromServer(
|
||||||
@ -72,6 +108,168 @@ const getLogbookDataFromServer = async (
|
|||||||
return hass.callApi<LogbookEntry[]>("GET", url);
|
return hass.callApi<LogbookEntry[]>("GET", url);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clearLogbookCache = (startDate, endDate) => {
|
export const clearLogbookCache = (startDate: string, endDate: string) => {
|
||||||
DATA_CACHE[`${startDate}${endDate}`] = {};
|
DATA_CACHE[`${startDate}${endDate}`] = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getLogbookMessage = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
state: string,
|
||||||
|
stateObj: HassEntity,
|
||||||
|
domain: string
|
||||||
|
): string => {
|
||||||
|
switch (domain) {
|
||||||
|
case "device_tracker":
|
||||||
|
case "person":
|
||||||
|
return state === "not_home"
|
||||||
|
? hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_away`)
|
||||||
|
: hass.localize(
|
||||||
|
`${LOGBOOK_LOCALIZE_PATH}.was_at_state`,
|
||||||
|
"state",
|
||||||
|
state
|
||||||
|
);
|
||||||
|
|
||||||
|
case "sun":
|
||||||
|
return state === "above_horizon"
|
||||||
|
? hass.localize(`${LOGBOOK_LOCALIZE_PATH}.rose`)
|
||||||
|
: hass.localize(`${LOGBOOK_LOCALIZE_PATH}.set`);
|
||||||
|
|
||||||
|
case "binary_sensor": {
|
||||||
|
const isOn = state === BINARY_STATE_ON;
|
||||||
|
const isOff = state === BINARY_STATE_OFF;
|
||||||
|
const device_class = stateObj.attributes.device_class;
|
||||||
|
|
||||||
|
switch (device_class) {
|
||||||
|
case "battery":
|
||||||
|
if (isOn) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_low`);
|
||||||
|
}
|
||||||
|
if (isOff) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_normal`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "connectivity":
|
||||||
|
if (isOn) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_connected`);
|
||||||
|
}
|
||||||
|
if (isOff) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_disconnected`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "door":
|
||||||
|
case "garage_door":
|
||||||
|
case "opening":
|
||||||
|
case "window":
|
||||||
|
if (isOn) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_opened`);
|
||||||
|
}
|
||||||
|
if (isOff) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_closed`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "lock":
|
||||||
|
if (isOn) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_unlocked`);
|
||||||
|
}
|
||||||
|
if (isOff) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_locked`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "plug":
|
||||||
|
if (isOn) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_plugged_in`);
|
||||||
|
}
|
||||||
|
if (isOff) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_unplugged`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "presence":
|
||||||
|
if (isOn) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_at_home`);
|
||||||
|
}
|
||||||
|
if (isOff) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_away`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "safety":
|
||||||
|
if (isOn) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_unsafe`);
|
||||||
|
}
|
||||||
|
if (isOff) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_safe`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "cold":
|
||||||
|
case "gas":
|
||||||
|
case "heat":
|
||||||
|
case "colightld":
|
||||||
|
case "moisture":
|
||||||
|
case "motion":
|
||||||
|
case "occupancy":
|
||||||
|
case "power":
|
||||||
|
case "problem":
|
||||||
|
case "smoke":
|
||||||
|
case "sound":
|
||||||
|
case "vibration":
|
||||||
|
if (isOn) {
|
||||||
|
return hass.localize(
|
||||||
|
`${LOGBOOK_LOCALIZE_PATH}.detected_device_class`,
|
||||||
|
"device_class",
|
||||||
|
device_class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (isOff) {
|
||||||
|
return hass.localize(
|
||||||
|
`${LOGBOOK_LOCALIZE_PATH}.cleared_device_class`,
|
||||||
|
"device_class",
|
||||||
|
device_class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "cover":
|
||||||
|
return state === "open"
|
||||||
|
? hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_opened`)
|
||||||
|
: hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_closed`);
|
||||||
|
|
||||||
|
case "lock":
|
||||||
|
if (state === "unlocked") {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_unlocked`);
|
||||||
|
}
|
||||||
|
if (state === "locked") {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_locked`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state === BINARY_STATE_ON) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.turned_on`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state === BINARY_STATE_OFF) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.turned_off`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UNAVAILABLE_STATES.includes(state)) {
|
||||||
|
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.became_unavailable`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hass.localize(
|
||||||
|
`${LOGBOOK_LOCALIZE_PATH}.changed_to_state`,
|
||||||
|
"state",
|
||||||
|
stateObj
|
||||||
|
? computeStateDisplay(hass.localize, stateObj, hass.language, state)
|
||||||
|
: state
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -60,6 +60,7 @@ export class MoreInfoLogbook extends LitElement {
|
|||||||
narrow
|
narrow
|
||||||
no-icon
|
no-icon
|
||||||
no-name
|
no-name
|
||||||
|
relative-time
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.entries=${this._logbookEntries}
|
.entries=${this._logbookEntries}
|
||||||
.userIdToName=${this._persons}
|
.userIdToName=${this._persons}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResultArray,
|
||||||
customElement,
|
customElement,
|
||||||
eventOptions,
|
eventOptions,
|
||||||
html,
|
html,
|
||||||
@ -15,12 +15,14 @@ import { formatDate } from "../../common/datetime/format_date";
|
|||||||
import { formatTimeWithSeconds } from "../../common/datetime/format_time";
|
import { formatTimeWithSeconds } from "../../common/datetime/format_time";
|
||||||
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
import { computeDomain } from "../../common/entity/compute_domain";
|
||||||
import { domainIcon } from "../../common/entity/domain_icon";
|
import { domainIcon } from "../../common/entity/domain_icon";
|
||||||
import { stateIcon } from "../../common/entity/state_icon";
|
|
||||||
import { computeRTL, emitRTLDirection } from "../../common/util/compute_rtl";
|
import { computeRTL, emitRTLDirection } from "../../common/util/compute_rtl";
|
||||||
import "../../components/ha-circular-progress";
|
import "../../components/ha-circular-progress";
|
||||||
import "../../components/ha-icon";
|
import "../../components/ha-icon";
|
||||||
|
import "../../components/ha-relative-time";
|
||||||
import { LogbookEntry } from "../../data/logbook";
|
import { LogbookEntry } from "../../data/logbook";
|
||||||
|
import { haStyle } from "../../resources/styles";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
@customElement("ha-logbook")
|
@customElement("ha-logbook")
|
||||||
@ -46,6 +48,9 @@ class HaLogbook extends LitElement {
|
|||||||
@property({ type: Boolean, attribute: "no-name" })
|
@property({ type: Boolean, attribute: "no-name" })
|
||||||
public noName = false;
|
public noName = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "relative-time" })
|
||||||
|
public relativeTime = false;
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@restoreScroll(".container") private _savedScrollPos?: number;
|
@restoreScroll(".container") private _savedScrollPos?: number;
|
||||||
|
|
||||||
@ -109,6 +114,7 @@ class HaLogbook extends LitElement {
|
|||||||
const state = item.entity_id ? this.hass.states[item.entity_id] : undefined;
|
const state = item.entity_id ? this.hass.states[item.entity_id] : undefined;
|
||||||
const item_username =
|
const item_username =
|
||||||
item.context_user_id && this.userIdToName[item.context_user_id];
|
item.context_user_id && this.userIdToName[item.context_user_id];
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="entry-container">
|
<div class="entry-container">
|
||||||
${index === 0 ||
|
${index === 0 ||
|
||||||
@ -123,58 +129,73 @@ class HaLogbook extends LitElement {
|
|||||||
`
|
`
|
||||||
: html``}
|
: html``}
|
||||||
|
|
||||||
<div class="entry">
|
<div class="entry ${classMap({ "no-entity": !item.entity_id })}">
|
||||||
<div class="time">
|
|
||||||
${formatTimeWithSeconds(new Date(item.when), this.hass.language)}
|
|
||||||
</div>
|
|
||||||
<div class="icon-message">
|
<div class="icon-message">
|
||||||
${!this.noIcon
|
${!this.noIcon
|
||||||
? html`
|
? html`
|
||||||
<ha-icon
|
<ha-icon
|
||||||
.icon=${state ? stateIcon(state) : domainIcon(item.domain)}
|
.icon=${domainIcon(
|
||||||
|
item.entity_id
|
||||||
|
? computeDomain(item.entity_id)
|
||||||
|
: item.domain,
|
||||||
|
state,
|
||||||
|
item.state
|
||||||
|
)}
|
||||||
></ha-icon>
|
></ha-icon>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<div class="message">
|
<div class="message-relative_time">
|
||||||
${!this.noName
|
<div class="message">
|
||||||
? !item.entity_id
|
${!this.noName
|
||||||
? html`<span class="name">${item.name}</span>`
|
? html`<a
|
||||||
: html`
|
href="#"
|
||||||
<a
|
@click=${this._entityClicked}
|
||||||
href="#"
|
.entityId=${item.entity_id}
|
||||||
@click=${this._entityClicked}
|
><span class="name">${item.name}</span></a
|
||||||
.entityId=${item.entity_id}
|
>`
|
||||||
class="name"
|
: ""}
|
||||||
>${item.name}</a
|
${item.message}
|
||||||
>
|
${item_username
|
||||||
`
|
? ` ${this.hass.localize(
|
||||||
: ""}
|
"ui.components.logbook.by"
|
||||||
${item.message}
|
)} ${item_username}`
|
||||||
${item_username
|
: !item.context_event_type
|
||||||
? ` by ${item_username}`
|
? ""
|
||||||
: !item.context_event_type
|
: item.context_event_type === "call_service"
|
||||||
? ""
|
? // Service Call
|
||||||
: item.context_event_type === "call_service"
|
` ${this.hass.localize("ui.components.logbook.by_service")}
|
||||||
? // Service Call
|
|
||||||
` by service
|
|
||||||
${item.context_domain}.${item.context_service}`
|
${item.context_domain}.${item.context_service}`
|
||||||
: item.context_entity_id === item.entity_id
|
: item.context_entity_id === item.entity_id
|
||||||
? // HomeKit or something that self references
|
? // HomeKit or something that self references
|
||||||
` by
|
` ${this.hass.localize("ui.components.logbook.by")}
|
||||||
${
|
${
|
||||||
item.context_name
|
item.context_name
|
||||||
? item.context_name
|
? item.context_name
|
||||||
: item.context_event_type
|
: item.context_event_type
|
||||||
}`
|
}`
|
||||||
: // Another entity such as an automation or script
|
: // Another entity such as an automation or script
|
||||||
html` by
|
html` ${this.hass.localize("ui.components.logbook.by")}
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
@click=${this._entityClicked}
|
@click=${this._entityClicked}
|
||||||
.entityId=${item.context_entity_id}
|
.entityId=${item.context_entity_id}
|
||||||
class="name"
|
class="name"
|
||||||
>${item.context_entity_id_name}</a
|
>${item.context_entity_id_name}</a
|
||||||
>`}
|
>`}
|
||||||
|
</div>
|
||||||
|
<div class="secondary">
|
||||||
|
<span
|
||||||
|
>${formatTimeWithSeconds(
|
||||||
|
new Date(item.when),
|
||||||
|
this.hass.language
|
||||||
|
)}</span
|
||||||
|
>
|
||||||
|
-
|
||||||
|
<ha-relative-time
|
||||||
|
.hass=${this.hass}
|
||||||
|
.datetime=${item.when}
|
||||||
|
></ha-relative-time>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -188,110 +209,131 @@ class HaLogbook extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _entityClicked(ev: Event) {
|
private _entityClicked(ev: Event) {
|
||||||
|
const entityId = (ev.currentTarget as any).entityId;
|
||||||
|
if (!entityId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
fireEvent(this, "hass-more-info", {
|
fireEvent(this, "hass-more-info", {
|
||||||
entityId: (ev.target as any).entityId,
|
entityId: entityId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResultArray {
|
||||||
return css`
|
return [
|
||||||
:host {
|
haStyle,
|
||||||
display: block;
|
css`
|
||||||
height: 100%;
|
:host {
|
||||||
}
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.rtl {
|
.rtl {
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry-container {
|
.entry-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry {
|
.entry {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
line-height: 2em;
|
line-height: 2em;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-top: 1px solid
|
border-top: 1px solid
|
||||||
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
|
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
|
||||||
}
|
}
|
||||||
|
|
||||||
.time {
|
.entry.no-entity,
|
||||||
display: flex;
|
.no-name .entry {
|
||||||
justify-content: center;
|
cursor: default;
|
||||||
flex-direction: column;
|
}
|
||||||
width: 75px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.date {
|
.entry:hover {
|
||||||
margin: 8px 0;
|
background-color: rgba(var(--rgb-primary-text-color), 0.04);
|
||||||
padding: 0 16px;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.narrow .date {
|
.narrow:not(.no-icon) .time {
|
||||||
padding: 0 8px;
|
margin-left: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rtl .date {
|
.message-relative_time {
|
||||||
direction: rtl;
|
display: flex;
|
||||||
}
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.icon-message {
|
.secondary {
|
||||||
display: flex;
|
font-size: 12px;
|
||||||
align-items: center;
|
line-height: 1.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-entries {
|
.date {
|
||||||
text-align: center;
|
margin: 8px 0;
|
||||||
color: var(--secondary-text-color);
|
padding: 0 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-icon {
|
.narrow .date {
|
||||||
margin: 0 8px 0 16px;
|
padding: 0 8px;
|
||||||
flex-shrink: 0;
|
}
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
.rtl .date {
|
||||||
color: var(--primary-text-color);
|
direction: rtl;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-name .message:first-letter {
|
.icon-message {
|
||||||
text-transform: capitalize;
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
.no-entries {
|
||||||
color: var(--primary-color);
|
text-align: center;
|
||||||
}
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
.uni-virtualizer-host {
|
ha-icon {
|
||||||
display: block;
|
margin-right: 16px;
|
||||||
position: relative;
|
flex-shrink: 0;
|
||||||
contain: strict;
|
color: var(--state-icon-color);
|
||||||
height: 100%;
|
}
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uni-virtualizer-host > * {
|
.message {
|
||||||
box-sizing: border-box;
|
color: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.narrow .entry {
|
.no-name .message:first-letter {
|
||||||
flex-direction: column;
|
text-transform: capitalize;
|
||||||
line-height: 1.5;
|
}
|
||||||
padding: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.narrow .icon-message ha-icon {
|
a {
|
||||||
margin-left: 0;
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
`;
|
|
||||||
|
.uni-virtualizer-host {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
contain: strict;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-virtualizer-host > * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.narrow .entry {
|
||||||
|
line-height: 1.5;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.narrow .icon-message ha-icon {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,18 +115,22 @@ export class HaPanelLogbook extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
${this._isLoading
|
${this._isLoading
|
||||||
? html`<div class="progress-wrapper">
|
? html`
|
||||||
<ha-circular-progress
|
<div class="progress-wrapper">
|
||||||
active
|
<ha-circular-progress
|
||||||
alt=${this.hass.localize("ui.common.loading")}
|
active
|
||||||
></ha-circular-progress>
|
alt=${this.hass.localize("ui.common.loading")}
|
||||||
</div>`
|
></ha-circular-progress>
|
||||||
: html`<ha-logbook
|
</div>
|
||||||
.hass=${this.hass}
|
`
|
||||||
.entries=${this._entries}
|
: html`
|
||||||
.userIdToName=${this._userIdToName}
|
<ha-logbook
|
||||||
virtualize
|
.hass=${this.hass}
|
||||||
></ha-logbook>`}
|
.entries=${this._entries}
|
||||||
|
.userIdToName=${this._userIdToName}
|
||||||
|
virtualize
|
||||||
|
></ha-logbook>
|
||||||
|
`}
|
||||||
</ha-app-layout>
|
</ha-app-layout>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -268,6 +272,7 @@ export class HaPanelLogbook extends LitElement {
|
|||||||
),
|
),
|
||||||
this._fetchUserDone,
|
this._fetchUserDone,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Fixed in TS 3.9 but upgrade out of scope for this PR.
|
// Fixed in TS 3.9 but upgrade out of scope for this PR.
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this._entries = entries;
|
this._entries = entries;
|
||||||
|
@ -285,7 +285,34 @@
|
|||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
"logbook": {
|
"logbook": {
|
||||||
"entries_not_found": "No logbook entries found."
|
"entries_not_found": "No logbook entries found.",
|
||||||
|
"by": "by",
|
||||||
|
"by_service": "by service",
|
||||||
|
"messages": {
|
||||||
|
"was_away": "was away",
|
||||||
|
"was_at_state": "was at {state}",
|
||||||
|
"rose": "rose",
|
||||||
|
"set": "set",
|
||||||
|
"was_low": "was low",
|
||||||
|
"was_normal": "was normal",
|
||||||
|
"was_connected": "was connected",
|
||||||
|
"was_disconnected": "was disconnected",
|
||||||
|
"was_opened": "was opened",
|
||||||
|
"was_closed": "was closed",
|
||||||
|
"was_unlocked": "was unlocked",
|
||||||
|
"was_locked": "was locked",
|
||||||
|
"was_plugged_in": "was plugged in",
|
||||||
|
"was_unplugged": "was unplugged",
|
||||||
|
"was_at_home": "was at home",
|
||||||
|
"was_unsafe": "was unsafe",
|
||||||
|
"was_safe": "was safe",
|
||||||
|
"detected_device_class": "detected {device_class}",
|
||||||
|
"cleared_device_class": "cleared (no {device_class} detected)",
|
||||||
|
"turned_off": "turned off",
|
||||||
|
"turned_on": "turned on",
|
||||||
|
"changed_to_state": "changed to {state}",
|
||||||
|
"became_unavailable": "became unavailable"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"entity": {
|
"entity": {
|
||||||
"entity-picker": {
|
"entity-picker": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user