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. */
|
||||
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. */
|
||||
export const DOMAINS_WITH_CARD = [
|
||||
"climate",
|
||||
@ -63,6 +123,10 @@ export const DOMAINS_MORE_INFO_NO_HISTORY = ["camera", "configurator", "scene"];
|
||||
/** States that we consider "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. */
|
||||
export const DOMAINS_TOGGLE = new Set([
|
||||
"fan",
|
||||
|
@ -2,9 +2,9 @@ import { HassEntity } from "home-assistant-js-websocket";
|
||||
|
||||
/** Return an icon representing a binary sensor state. */
|
||||
|
||||
export const binarySensorIcon = (state: HassEntity) => {
|
||||
const is_off = state.state && state.state === "off";
|
||||
switch (state.attributes.device_class) {
|
||||
export const binarySensorIcon = (state?: string, stateObj?: HassEntity) => {
|
||||
const is_off = state === "off";
|
||||
switch (stateObj?.attributes.device_class) {
|
||||
case "battery":
|
||||
return is_off ? "hass:battery" : "hass:battery-outline";
|
||||
case "battery_charging":
|
||||
|
@ -9,14 +9,17 @@ import { computeStateDomain } from "./compute_state_domain";
|
||||
export const computeStateDisplay = (
|
||||
localize: LocalizeFunc,
|
||||
stateObj: HassEntity,
|
||||
language: string
|
||||
language: string,
|
||||
state?: string
|
||||
): string => {
|
||||
if (stateObj.state === UNKNOWN || stateObj.state === UNAVAILABLE) {
|
||||
return localize(`state.default.${stateObj.state}`);
|
||||
const compareState = state !== undefined ? state : stateObj.state;
|
||||
|
||||
if (compareState === UNKNOWN || compareState === UNAVAILABLE) {
|
||||
return localize(`state.default.${compareState}`);
|
||||
}
|
||||
|
||||
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);
|
||||
@ -56,7 +59,7 @@ export const computeStateDisplay = (
|
||||
}
|
||||
|
||||
if (domain === "humidifier") {
|
||||
if (stateObj.state === "on" && stateObj.attributes.humidity) {
|
||||
if (compareState === "on" && stateObj.attributes.humidity) {
|
||||
return `${stateObj.attributes.humidity}%`;
|
||||
}
|
||||
}
|
||||
@ -65,11 +68,11 @@ export const computeStateDisplay = (
|
||||
// Return device class translation
|
||||
(stateObj.attributes.device_class &&
|
||||
localize(
|
||||
`component.${domain}.state.${stateObj.attributes.device_class}.${stateObj.state}`
|
||||
`component.${domain}.state.${stateObj.attributes.device_class}.${compareState}`
|
||||
)) ||
|
||||
// Return default translation
|
||||
localize(`component.${domain}.state._.${stateObj.state}`) ||
|
||||
localize(`component.${domain}.state._.${compareState}`) ||
|
||||
// We don't know! Return the raw state.
|
||||
stateObj.state
|
||||
compareState
|
||||
);
|
||||
};
|
||||
|
@ -1,13 +1,12 @@
|
||||
/** Return an icon representing a cover state. */
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { domainIcon } from "./domain_icon";
|
||||
|
||||
export const coverIcon = (state: HassEntity): string => {
|
||||
const open = state.state !== "closed";
|
||||
export const coverIcon = (state?: string, stateObj?: HassEntity): string => {
|
||||
const open = state !== "closed";
|
||||
|
||||
switch (state.attributes.device_class) {
|
||||
switch (stateObj?.attributes.device_class) {
|
||||
case "garage":
|
||||
switch (state.state) {
|
||||
switch (state) {
|
||||
case "opening":
|
||||
return "hass:arrow-up-box";
|
||||
case "closing":
|
||||
@ -18,7 +17,7 @@ export const coverIcon = (state: HassEntity): string => {
|
||||
return "hass:garage-open";
|
||||
}
|
||||
case "gate":
|
||||
switch (state.state) {
|
||||
switch (state) {
|
||||
case "opening":
|
||||
case "closing":
|
||||
return "hass:gate-arrow-right";
|
||||
@ -32,7 +31,7 @@ export const coverIcon = (state: HassEntity): string => {
|
||||
case "damper":
|
||||
return open ? "hass:circle" : "hass:circle-slice-8";
|
||||
case "shutter":
|
||||
switch (state.state) {
|
||||
switch (state) {
|
||||
case "opening":
|
||||
return "hass:arrow-up-box";
|
||||
case "closing":
|
||||
@ -44,7 +43,7 @@ export const coverIcon = (state: HassEntity): string => {
|
||||
}
|
||||
case "blind":
|
||||
case "curtain":
|
||||
switch (state.state) {
|
||||
switch (state) {
|
||||
case "opening":
|
||||
return "hass:arrow-up-box";
|
||||
case "closing":
|
||||
@ -55,7 +54,7 @@ export const coverIcon = (state: HassEntity): string => {
|
||||
return "hass:blinds-open";
|
||||
}
|
||||
case "window":
|
||||
switch (state.state) {
|
||||
switch (state) {
|
||||
case "opening":
|
||||
return "hass:arrow-up-box";
|
||||
case "closing":
|
||||
@ -65,7 +64,16 @@ export const coverIcon = (state: HassEntity): string => {
|
||||
default:
|
||||
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:
|
||||
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.
|
||||
*
|
||||
* 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 = {
|
||||
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 domainIcon = (domain: string, state?: string): string => {
|
||||
if (domain in fixedIcons) {
|
||||
return fixedIcons[domain];
|
||||
}
|
||||
export const domainIcon = (
|
||||
domain: string,
|
||||
stateObj?: HassEntity,
|
||||
state?: string
|
||||
): string => {
|
||||
const compareState = state !== undefined ? state : stateObj?.state;
|
||||
|
||||
switch (domain) {
|
||||
case "alarm_control_panel":
|
||||
switch (state) {
|
||||
switch (compareState) {
|
||||
case "armed_home":
|
||||
return "hass:bell-plus";
|
||||
case "armed_night":
|
||||
@ -71,21 +32,10 @@ export const domainIcon = (domain: string, state?: string): string => {
|
||||
}
|
||||
|
||||
case "binary_sensor":
|
||||
return state && state === "off"
|
||||
? "hass:radiobox-blank"
|
||||
: "hass:checkbox-marked-circle";
|
||||
return binarySensorIcon(compareState, stateObj);
|
||||
|
||||
case "cover":
|
||||
switch (state) {
|
||||
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";
|
||||
}
|
||||
return coverIcon(compareState, stateObj);
|
||||
|
||||
case "humidifier":
|
||||
return state && state === "off"
|
||||
@ -93,13 +43,13 @@ export const domainIcon = (domain: string, state?: string): string => {
|
||||
: "hass:air-humidifier";
|
||||
|
||||
case "lock":
|
||||
return state && state === "unlocked" ? "hass:lock-open" : "hass:lock";
|
||||
return compareState === "unlocked" ? "hass:lock-open" : "hass:lock";
|
||||
|
||||
case "media_player":
|
||||
return state && state === "playing" ? "hass:cast-connected" : "hass:cast";
|
||||
return compareState === "playing" ? "hass:cast-connected" : "hass:cast";
|
||||
|
||||
case "zwave":
|
||||
switch (state) {
|
||||
switch (compareState) {
|
||||
case "dead":
|
||||
return "hass:emoticon-dead";
|
||||
case "sleeping":
|
||||
@ -110,11 +60,32 @@ export const domainIcon = (domain: string, state?: string): string => {
|
||||
return "hass:z-wave";
|
||||
}
|
||||
|
||||
default:
|
||||
case "sensor": {
|
||||
const icon = sensorIcon(stateObj);
|
||||
if (icon) {
|
||||
return 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 + " (" + state + ")"
|
||||
"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. */
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { UNIT_C, UNIT_F } from "../const";
|
||||
import { domainIcon } from "./domain_icon";
|
||||
import { FIXED_DEVICE_CLASS_ICONS, UNIT_C, UNIT_F } from "../const";
|
||||
import { batteryIcon } from "./battery_icon";
|
||||
|
||||
const fixedDeviceClassIcons = {
|
||||
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",
|
||||
};
|
||||
export const sensorIcon = (stateObj?: HassEntity): string | undefined => {
|
||||
const dclass = stateObj?.attributes.device_class;
|
||||
|
||||
export const sensorIcon = (state: HassEntity) => {
|
||||
const dclass = state.attributes.device_class;
|
||||
|
||||
if (dclass && dclass in fixedDeviceClassIcons) {
|
||||
return fixedDeviceClassIcons[dclass];
|
||||
if (dclass && dclass in FIXED_DEVICE_CLASS_ICONS) {
|
||||
return FIXED_DEVICE_CLASS_ICONS[dclass];
|
||||
}
|
||||
|
||||
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) {
|
||||
return "hass:thermometer";
|
||||
}
|
||||
return domainIcon("sensor");
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
@ -1,19 +1,8 @@
|
||||
/** Return an icon representing a state. */
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { DEFAULT_DOMAIN_ICON } from "../const";
|
||||
import { binarySensorIcon } from "./binary_sensor_icon";
|
||||
import { computeDomain } from "./compute_domain";
|
||||
import { coverIcon } from "./cover_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) => {
|
||||
if (!state) {
|
||||
@ -23,10 +12,5 @@ export const stateIcon = (state: HassEntity) => {
|
||||
return state.attributes.icon;
|
||||
}
|
||||
|
||||
const domain = computeDomain(state.entity_id);
|
||||
|
||||
if (domain in domainIcons) {
|
||||
return domainIcons[domain](state);
|
||||
}
|
||||
return domainIcon(domain, state.state);
|
||||
return domainIcon(computeDomain(state.entity_id), state);
|
||||
};
|
||||
|
@ -147,7 +147,7 @@ export class HaStateLabelBadge extends LitElement {
|
||||
return "hass:alert-circle";
|
||||
}
|
||||
// state == 'disarmed'
|
||||
return domainIcon(domain, state.state);
|
||||
return domainIcon(domain, state);
|
||||
case "binary_sensor":
|
||||
case "device_tracker":
|
||||
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 { UNAVAILABLE_STATES } from "./entity";
|
||||
|
||||
const LOGBOOK_LOCALIZE_PATH = "ui.components.logbook.messages";
|
||||
|
||||
export interface LogbookEntry {
|
||||
when: string;
|
||||
name: string;
|
||||
message: string;
|
||||
message?: string;
|
||||
entity_id?: string;
|
||||
domain: string;
|
||||
context_user_id?: string;
|
||||
@ -13,13 +20,43 @@ export interface LogbookEntry {
|
||||
context_entity_id?: string;
|
||||
context_entity_id_name?: string;
|
||||
context_name?: string;
|
||||
state?: string;
|
||||
}
|
||||
|
||||
const DATA_CACHE: {
|
||||
[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,
|
||||
startDate: string,
|
||||
endDate: string,
|
||||
@ -43,9 +80,8 @@ export const getLogbookData = (
|
||||
}
|
||||
|
||||
if (entityId !== ALL_ENTITIES && DATA_CACHE[cacheKey][ALL_ENTITIES]) {
|
||||
return DATA_CACHE[cacheKey][ALL_ENTITIES].then((entities) =>
|
||||
entities.filter((entity) => entity.entity_id === entityId)
|
||||
);
|
||||
const entities = await DATA_CACHE[cacheKey][ALL_ENTITIES];
|
||||
return entities.filter((entity) => entity.entity_id === entityId);
|
||||
}
|
||||
|
||||
DATA_CACHE[cacheKey][entityId] = getLogbookDataFromServer(
|
||||
@ -72,6 +108,168 @@ const getLogbookDataFromServer = async (
|
||||
return hass.callApi<LogbookEntry[]>("GET", url);
|
||||
};
|
||||
|
||||
export const clearLogbookCache = (startDate, endDate) => {
|
||||
export const clearLogbookCache = (startDate: string, endDate: string) => {
|
||||
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
|
||||
no-icon
|
||||
no-name
|
||||
relative-time
|
||||
.hass=${this.hass}
|
||||
.entries=${this._logbookEntries}
|
||||
.userIdToName=${this._persons}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
CSSResultArray,
|
||||
customElement,
|
||||
eventOptions,
|
||||
html,
|
||||
@ -15,12 +15,14 @@ import { formatDate } from "../../common/datetime/format_date";
|
||||
import { formatTimeWithSeconds } from "../../common/datetime/format_time";
|
||||
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../common/entity/compute_domain";
|
||||
import { domainIcon } from "../../common/entity/domain_icon";
|
||||
import { stateIcon } from "../../common/entity/state_icon";
|
||||
import { computeRTL, emitRTLDirection } from "../../common/util/compute_rtl";
|
||||
import "../../components/ha-circular-progress";
|
||||
import "../../components/ha-icon";
|
||||
import "../../components/ha-relative-time";
|
||||
import { LogbookEntry } from "../../data/logbook";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import { HomeAssistant } from "../../types";
|
||||
|
||||
@customElement("ha-logbook")
|
||||
@ -46,6 +48,9 @@ class HaLogbook extends LitElement {
|
||||
@property({ type: Boolean, attribute: "no-name" })
|
||||
public noName = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "relative-time" })
|
||||
public relativeTime = false;
|
||||
|
||||
// @ts-ignore
|
||||
@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 item_username =
|
||||
item.context_user_id && this.userIdToName[item.context_user_id];
|
||||
|
||||
return html`
|
||||
<div class="entry-container">
|
||||
${index === 0 ||
|
||||
@ -123,51 +129,52 @@ class HaLogbook extends LitElement {
|
||||
`
|
||||
: html``}
|
||||
|
||||
<div class="entry">
|
||||
<div class="time">
|
||||
${formatTimeWithSeconds(new Date(item.when), this.hass.language)}
|
||||
</div>
|
||||
<div class="entry ${classMap({ "no-entity": !item.entity_id })}">
|
||||
<div class="icon-message">
|
||||
${!this.noIcon
|
||||
? html`
|
||||
<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>
|
||||
`
|
||||
: ""}
|
||||
<div class="message-relative_time">
|
||||
<div class="message">
|
||||
${!this.noName
|
||||
? !item.entity_id
|
||||
? html`<span class="name">${item.name}</span>`
|
||||
: html`
|
||||
<a
|
||||
? html`<a
|
||||
href="#"
|
||||
@click=${this._entityClicked}
|
||||
.entityId=${item.entity_id}
|
||||
class="name"
|
||||
>${item.name}</a
|
||||
>
|
||||
`
|
||||
><span class="name">${item.name}</span></a
|
||||
>`
|
||||
: ""}
|
||||
${item.message}
|
||||
${item_username
|
||||
? ` by ${item_username}`
|
||||
? ` ${this.hass.localize(
|
||||
"ui.components.logbook.by"
|
||||
)} ${item_username}`
|
||||
: !item.context_event_type
|
||||
? ""
|
||||
: item.context_event_type === "call_service"
|
||||
? // Service Call
|
||||
` by service
|
||||
` ${this.hass.localize("ui.components.logbook.by_service")}
|
||||
${item.context_domain}.${item.context_service}`
|
||||
: item.context_entity_id === item.entity_id
|
||||
? // HomeKit or something that self references
|
||||
` by
|
||||
` ${this.hass.localize("ui.components.logbook.by")}
|
||||
${
|
||||
item.context_name
|
||||
? item.context_name
|
||||
: item.context_event_type
|
||||
}`
|
||||
: // Another entity such as an automation or script
|
||||
html` by
|
||||
html` ${this.hass.localize("ui.components.logbook.by")}
|
||||
<a
|
||||
href="#"
|
||||
@click=${this._entityClicked}
|
||||
@ -176,6 +183,20 @@ class HaLogbook extends LitElement {
|
||||
>${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>
|
||||
@ -188,14 +209,22 @@ class HaLogbook extends LitElement {
|
||||
}
|
||||
|
||||
private _entityClicked(ev: Event) {
|
||||
const entityId = (ev.currentTarget as any).entityId;
|
||||
if (!entityId) {
|
||||
return;
|
||||
}
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
fireEvent(this, "hass-more-info", {
|
||||
entityId: (ev.target as any).entityId,
|
||||
entityId: entityId,
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
static get styles(): CSSResultArray {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
height: 100%;
|
||||
@ -219,14 +248,27 @@ class HaLogbook extends LitElement {
|
||||
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
|
||||
}
|
||||
|
||||
.time {
|
||||
.entry.no-entity,
|
||||
.no-name .entry {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.entry:hover {
|
||||
background-color: rgba(var(--rgb-primary-text-color), 0.04);
|
||||
}
|
||||
|
||||
.narrow:not(.no-icon) .time {
|
||||
margin-left: 32px;
|
||||
}
|
||||
|
||||
.message-relative_time {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
width: 75px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.date {
|
||||
@ -253,9 +295,9 @@ class HaLogbook extends LitElement {
|
||||
}
|
||||
|
||||
ha-icon {
|
||||
margin: 0 8px 0 16px;
|
||||
margin-right: 16px;
|
||||
flex-shrink: 0;
|
||||
color: var(--primary-text-color);
|
||||
color: var(--state-icon-color);
|
||||
}
|
||||
|
||||
.message {
|
||||
@ -283,15 +325,15 @@ class HaLogbook extends LitElement {
|
||||
}
|
||||
|
||||
.narrow .entry {
|
||||
flex-direction: column;
|
||||
line-height: 1.5;
|
||||
padding: 8px 0;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.narrow .icon-message ha-icon {
|
||||
margin-left: 0;
|
||||
}
|
||||
`;
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,18 +115,22 @@ export class HaPanelLogbook extends LitElement {
|
||||
</div>
|
||||
|
||||
${this._isLoading
|
||||
? html`<div class="progress-wrapper">
|
||||
? html`
|
||||
<div class="progress-wrapper">
|
||||
<ha-circular-progress
|
||||
active
|
||||
alt=${this.hass.localize("ui.common.loading")}
|
||||
></ha-circular-progress>
|
||||
</div>`
|
||||
: html`<ha-logbook
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
<ha-logbook
|
||||
.hass=${this.hass}
|
||||
.entries=${this._entries}
|
||||
.userIdToName=${this._userIdToName}
|
||||
virtualize
|
||||
></ha-logbook>`}
|
||||
></ha-logbook>
|
||||
`}
|
||||
</ha-app-layout>
|
||||
`;
|
||||
}
|
||||
@ -268,6 +272,7 @@ export class HaPanelLogbook extends LitElement {
|
||||
),
|
||||
this._fetchUserDone,
|
||||
]);
|
||||
|
||||
// Fixed in TS 3.9 but upgrade out of scope for this PR.
|
||||
// @ts-ignore
|
||||
this._entries = entries;
|
||||
|
@ -285,7 +285,34 @@
|
||||
},
|
||||
"components": {
|
||||
"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-picker": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user