diff --git a/src/common/const.ts b/src/common/const.ts index b770cf1d447b..6c3b4faa1fc4 100644 --- a/src/common/const.ts +++ b/src/common/const.ts @@ -1,6 +1,7 @@ /** Constants to be used in the frontend. */ import { + mdiAccount, mdiAirFilter, mdiAlert, mdiAngleAcute, @@ -50,8 +51,10 @@ import { mdiProgressClock, mdiRayVertex, mdiRemote, + mdiRobot, mdiRobotMower, mdiRobotVacuum, + mdiRoomService, mdiScriptText, mdiSineWave, mdiSpeakerMessage, @@ -61,6 +64,7 @@ import { mdiThermometerLines, mdiThermostat, mdiTimerOutline, + mdiToggleSwitch, mdiTransmissionTower, mdiWater, mdiWaterPercent, @@ -69,6 +73,7 @@ import { mdiWeatherRainy, mdiWeatherWindy, mdiWeight, + mdiWhiteBalanceSunny, mdiWifi, } from "@mdi/js"; @@ -78,6 +83,9 @@ import { mdiHomeAssistant } from "../resources/home-assistant-logo-svg"; // Arrays with values should be alphabetically sorted if order doesn't matter. // Each constant should have a description what it is supposed to be used for. +/** Icon to use when no icon specified for service. */ +export const DEFAULT_SERVICE_ICON = mdiRoomService; + /** Icon to use when no icon specified for domain. */ export const DEFAULT_DOMAIN_ICON = mdiBookmark; @@ -85,20 +93,23 @@ export const DEFAULT_DOMAIN_ICON = mdiBookmark; export const FIXED_DOMAIN_ICONS = { air_quality: mdiAirFilter, alert: mdiAlert, + automation: mdiRobot, calendar: mdiCalendar, climate: mdiThermostat, configurator: mdiCog, conversation: mdiMicrophoneMessage, counter: mdiCounter, - datetime: mdiCalendarClock, date: mdiCalendar, + datetime: mdiCalendarClock, demo: mdiHomeAssistant, + device_tracker: mdiAccount, google_assistant: mdiGoogleAssistant, group: mdiGoogleCirclesCommunities, homeassistant: mdiHomeAssistant, homekit: mdiHomeAutomation, - image: mdiImage, image_processing: mdiImageFilterFrames, + image: mdiImage, + input_boolean: mdiToggleSwitch, input_button: mdiButtonPointer, input_datetime: mdiCalendarClock, input_number: mdiRayVertex, @@ -110,6 +121,7 @@ export const FIXED_DOMAIN_ICONS = { notify: mdiCommentAlert, number: mdiRayVertex, persistent_notification: mdiBell, + person: mdiAccount, plant: mdiFlower, proximity: mdiAppleSafari, remote: mdiRemote, @@ -121,10 +133,11 @@ export const FIXED_DOMAIN_ICONS = { simple_alarm: mdiBell, siren: mdiBullhorn, stt: mdiMicrophoneMessage, + sun: mdiWhiteBalanceSunny, text: mdiFormTextbox, - todo: mdiClipboardList, time: mdiClock, timer: mdiTimerOutline, + todo: mdiClipboardList, tts: mdiSpeakerMessage, vacuum: mdiRobotVacuum, wake_word: mdiChatSleep, diff --git a/src/common/entity/alarm_panel_icon.ts b/src/common/entity/alarm_panel_icon.ts deleted file mode 100644 index cdbb736b3a4d..000000000000 --- a/src/common/entity/alarm_panel_icon.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** Return an icon representing a alarm panel state. */ - -import { - mdiShieldLock, - mdiShieldAirplane, - mdiShieldHome, - mdiShieldMoon, - mdiSecurity, - mdiShieldOutline, - mdiBellRing, - mdiShieldOff, - mdiShield, -} from "@mdi/js"; - -export const alarmPanelIcon = (state?: string) => { - switch (state) { - case "armed_away": - return mdiShieldLock; - case "armed_vacation": - return mdiShieldAirplane; - case "armed_home": - return mdiShieldHome; - case "armed_night": - return mdiShieldMoon; - case "armed_custom_bypass": - return mdiSecurity; - case "pending": - return mdiShieldOutline; - case "triggered": - return mdiBellRing; - case "disarmed": - return mdiShieldOff; - default: - return mdiShield; - } -}; diff --git a/src/common/entity/battery_icon.ts b/src/common/entity/battery_icon.ts index d73cdbe489b4..75509b5d8b27 100644 --- a/src/common/entity/battery_icon.ts +++ b/src/common/entity/battery_icon.ts @@ -1,92 +1,59 @@ -/** Return an icon representing a battery state. */ -import { - mdiBattery, - mdiBattery10, - mdiBattery20, - mdiBattery30, - mdiBattery40, - mdiBattery50, - mdiBattery60, - mdiBattery70, - mdiBattery80, - mdiBattery90, - mdiBatteryAlert, - mdiBatteryAlertVariantOutline, - mdiBatteryCharging, - mdiBatteryCharging10, - mdiBatteryCharging20, - mdiBatteryCharging30, - mdiBatteryCharging40, - mdiBatteryCharging50, - mdiBatteryCharging60, - mdiBatteryCharging70, - mdiBatteryCharging80, - mdiBatteryCharging90, - mdiBatteryChargingOutline, - mdiBatteryUnknown, -} from "@mdi/js"; import { HassEntity } from "home-assistant-js-websocket"; const BATTERY_ICONS = { - 10: mdiBattery10, - 20: mdiBattery20, - 30: mdiBattery30, - 40: mdiBattery40, - 50: mdiBattery50, - 60: mdiBattery60, - 70: mdiBattery70, - 80: mdiBattery80, - 90: mdiBattery90, - 100: mdiBattery, + 10: "mdi:battery-10", + 20: "mdi:battery-20", + 30: "mdi:battery-30", + 40: "mdi:battery-40", + 50: "mdi:battery-50", + 60: "mdi:battery-60", + 70: "mdi:battery-70", + 80: "mdi:battery-80", + 90: "mdi:battery-90", + 100: "mdi:battery", }; const BATTERY_CHARGING_ICONS = { - 10: mdiBatteryCharging10, - 20: mdiBatteryCharging20, - 30: mdiBatteryCharging30, - 40: mdiBatteryCharging40, - 50: mdiBatteryCharging50, - 60: mdiBatteryCharging60, - 70: mdiBatteryCharging70, - 80: mdiBatteryCharging80, - 90: mdiBatteryCharging90, - 100: mdiBatteryCharging, + 10: "mdi:battery-charging-10", + 20: "mdi:battery-charging-20", + 30: "mdi:battery-charging-30", + 40: "mdi:battery-charging-40", + 50: "mdi:battery-charging-50", + 60: "mdi:battery-charging-60", + 70: "mdi:battery-charging-70", + 80: "mdi:battery-charging-80", + 90: "mdi:battery-charging-90", + 100: "mdi:battery-charging", }; -export const batteryStateIcon = ( - batteryState: HassEntity, - batteryChargingState?: HassEntity -) => { - const battery = batteryState.state; - const batteryCharging = - batteryChargingState && batteryChargingState.state === "on"; - - return batteryIcon(battery, batteryCharging); +export const batteryIcon = (stateObj: HassEntity, state?: string) => { + const level = state ?? stateObj.state; + return batteryLevelIcon(level); }; -export const batteryIcon = ( - batteryState: number | string, - batteryCharging?: boolean -) => { - const batteryValue = Number(batteryState); +export const batteryLevelIcon = ( + batteryLevel: number | string, + isBatteryCharging?: boolean +): string => { + const batteryValue = Number(batteryLevel); if (isNaN(batteryValue)) { - if (batteryState === "off") { - return mdiBattery; + if (batteryLevel === "off") { + return "mdi:battery"; } - if (batteryState === "on") { - return mdiBatteryAlert; + if (batteryLevel === "on") { + return "mdi:battery-alert"; } - return mdiBatteryUnknown; + return "mdi:battery-unknown"; } const batteryRound = Math.round(batteryValue / 10) * 10; - if (batteryCharging && batteryValue >= 10) { + if (isBatteryCharging && batteryValue >= 10) { return BATTERY_CHARGING_ICONS[batteryRound]; } - if (batteryCharging) { - return mdiBatteryChargingOutline; + if (isBatteryCharging) { + return "mdi:battery-charging-outline"; } if (batteryValue <= 5) { - return mdiBatteryAlertVariantOutline; + return "mdi:battery-alert-variant-outline"; } return BATTERY_ICONS[batteryRound]; }; diff --git a/src/common/entity/binary_sensor_icon.ts b/src/common/entity/binary_sensor_icon.ts deleted file mode 100644 index fa2d81bda370..000000000000 --- a/src/common/entity/binary_sensor_icon.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { - mdiAlertCircle, - mdiBattery, - mdiBatteryCharging, - mdiBatteryOutline, - mdiBrightness5, - mdiBrightness7, - mdiCheckboxMarkedCircle, - mdiCheckNetworkOutline, - mdiCloseNetworkOutline, - mdiCheckCircle, - mdiCropPortrait, - mdiDoorClosed, - mdiDoorOpen, - mdiFire, - mdiGarage, - mdiGarageOpen, - mdiHome, - mdiHomeOutline, - mdiLock, - mdiLockOpen, - mdiMusicNote, - mdiMusicNoteOff, - mdiMotionSensor, - mdiMotionSensorOff, - mdiPackage, - mdiPackageUp, - mdiPlay, - mdiPowerPlug, - mdiPowerPlugOff, - mdiRadioboxBlank, - mdiSnowflake, - mdiSmokeDetector, - mdiSmokeDetectorAlert, - mdiSmokeDetectorVariant, - mdiSmokeDetectorVariantAlert, - mdiSquare, - mdiSquareOutline, - mdiStop, - mdiThermometer, - mdiVibrate, - mdiWater, - mdiWaterOff, - mdiWindowClosed, - mdiWindowOpen, -} from "@mdi/js"; -import { HassEntity } from "home-assistant-js-websocket"; - -/** Return an icon representing a binary sensor state. */ - -export const binarySensorIcon = (state?: string, stateObj?: HassEntity) => { - const is_off = state === "off"; - switch (stateObj?.attributes.device_class) { - case "battery": - return is_off ? mdiBattery : mdiBatteryOutline; - case "battery_charging": - return is_off ? mdiBattery : mdiBatteryCharging; - case "carbon_monoxide": - return is_off ? mdiSmokeDetector : mdiSmokeDetectorAlert; - case "cold": - return is_off ? mdiThermometer : mdiSnowflake; - case "connectivity": - return is_off ? mdiCloseNetworkOutline : mdiCheckNetworkOutline; - case "door": - return is_off ? mdiDoorClosed : mdiDoorOpen; - case "garage_door": - return is_off ? mdiGarage : mdiGarageOpen; - case "power": - return is_off ? mdiPowerPlugOff : mdiPowerPlug; - case "gas": - case "problem": - case "safety": - case "tamper": - return is_off ? mdiCheckCircle : mdiAlertCircle; - case "smoke": - return is_off ? mdiSmokeDetectorVariant : mdiSmokeDetectorVariantAlert; - case "heat": - return is_off ? mdiThermometer : mdiFire; - case "light": - return is_off ? mdiBrightness5 : mdiBrightness7; - case "lock": - return is_off ? mdiLock : mdiLockOpen; - case "moisture": - return is_off ? mdiWaterOff : mdiWater; - case "motion": - return is_off ? mdiMotionSensorOff : mdiMotionSensor; - case "occupancy": - return is_off ? mdiHomeOutline : mdiHome; - case "opening": - return is_off ? mdiSquare : mdiSquareOutline; - case "plug": - return is_off ? mdiPowerPlugOff : mdiPowerPlug; - case "presence": - return is_off ? mdiHomeOutline : mdiHome; - case "running": - return is_off ? mdiStop : mdiPlay; - case "sound": - return is_off ? mdiMusicNoteOff : mdiMusicNote; - case "update": - return is_off ? mdiPackage : mdiPackageUp; - case "vibration": - return is_off ? mdiCropPortrait : mdiVibrate; - case "window": - return is_off ? mdiWindowClosed : mdiWindowOpen; - default: - return is_off ? mdiRadioboxBlank : mdiCheckboxMarkedCircle; - } -}; diff --git a/src/common/entity/cover_icon.ts b/src/common/entity/cover_icon.ts index bee06d61015f..53a30db752ca 100644 --- a/src/common/entity/cover_icon.ts +++ b/src/common/entity/cover_icon.ts @@ -1,132 +1,12 @@ /** Return an icon representing a cover state. */ import { - mdiArrowUpBox, - mdiArrowDownBox, - mdiGarage, - mdiGarageOpen, - mdiGateArrowRight, - mdiGate, - mdiGateOpen, - mdiDoorOpen, - mdiDoorClosed, - mdiCircle, - mdiWindowShutter, - mdiWindowShutterOpen, - mdiBlindsHorizontal, - mdiBlindsHorizontalClosed, - mdiRollerShade, - mdiRollerShadeClosed, - mdiWindowClosed, - mdiWindowOpen, - mdiArrowExpandHorizontal, - mdiArrowUp, mdiArrowCollapseHorizontal, mdiArrowDown, - mdiCircleSlice8, - mdiArrowSplitVertical, - mdiCurtains, - mdiCurtainsClosed, + mdiArrowExpandHorizontal, + mdiArrowUp, } from "@mdi/js"; import { HassEntity } from "home-assistant-js-websocket"; -export const coverIcon = (state?: string, stateObj?: HassEntity): string => { - const open = state !== "closed"; - - switch (stateObj?.attributes.device_class) { - case "garage": - switch (state) { - case "opening": - return mdiArrowUpBox; - case "closing": - return mdiArrowDownBox; - case "closed": - return mdiGarage; - default: - return mdiGarageOpen; - } - case "gate": - switch (state) { - case "opening": - case "closing": - return mdiGateArrowRight; - case "closed": - return mdiGate; - default: - return mdiGateOpen; - } - case "door": - return open ? mdiDoorOpen : mdiDoorClosed; - case "damper": - return open ? mdiCircle : mdiCircleSlice8; - case "shutter": - switch (state) { - case "opening": - return mdiArrowUpBox; - case "closing": - return mdiArrowDownBox; - case "closed": - return mdiWindowShutter; - default: - return mdiWindowShutterOpen; - } - case "curtain": - switch (state) { - case "opening": - return mdiArrowSplitVertical; - case "closing": - return mdiArrowCollapseHorizontal; - case "closed": - return mdiCurtainsClosed; - default: - return mdiCurtains; - } - case "blind": - switch (state) { - case "opening": - return mdiArrowUpBox; - case "closing": - return mdiArrowDownBox; - case "closed": - return mdiBlindsHorizontalClosed; - default: - return mdiBlindsHorizontal; - } - case "shade": - switch (state) { - case "opening": - return mdiArrowUpBox; - case "closing": - return mdiArrowDownBox; - case "closed": - return mdiRollerShadeClosed; - default: - return mdiRollerShade; - } - case "window": - switch (state) { - case "opening": - return mdiArrowUpBox; - case "closing": - return mdiArrowDownBox; - case "closed": - return mdiWindowClosed; - default: - return mdiWindowOpen; - } - } - - switch (state) { - case "opening": - return mdiArrowUpBox; - case "closing": - return mdiArrowDownBox; - case "closed": - return mdiWindowClosed; - default: - return mdiWindowOpen; - } -}; - export const computeOpenIcon = (stateObj: HassEntity): string => { switch (stateObj.attributes.device_class) { case "awning": diff --git a/src/common/entity/device_tracker_icon.ts b/src/common/entity/device_tracker_icon.ts new file mode 100644 index 000000000000..02789242391f --- /dev/null +++ b/src/common/entity/device_tracker_icon.ts @@ -0,0 +1,16 @@ +import { HassEntity } from "home-assistant-js-websocket"; + +export const deviceTrackerIcon = (stateObj: HassEntity, state?: string) => { + const compareState = state ?? stateObj.state; + if (stateObj?.attributes.source_type === "router") { + return compareState === "home" ? "mdi:lan-connect" : "mdi:lan-disconnect"; + } + if ( + ["bluetooth", "bluetooth_le"].includes(stateObj?.attributes.source_type) + ) { + return compareState === "home" ? "mdi:bluetooth-connect" : "mdi:bluetooth"; + } + return compareState === "not_home" + ? "mdi:account-arrow-right" + : "mdi:account"; +}; diff --git a/src/common/entity/domain_icon.ts b/src/common/entity/domain_icon.ts deleted file mode 100644 index dfbbc69a5e17..000000000000 --- a/src/common/entity/domain_icon.ts +++ /dev/null @@ -1,301 +0,0 @@ -import { - mdiAccount, - mdiAccountArrowRight, - mdiAirHumidifier, - mdiAirHumidifierOff, - mdiAudioVideo, - mdiAudioVideoOff, - mdiBluetooth, - mdiBluetoothConnect, - mdiButtonPointer, - mdiCalendar, - mdiCast, - mdiCastConnected, - mdiCastOff, - mdiChartSankey, - mdiCheckCircleOutline, - mdiClock, - mdiCloseCircleOutline, - mdiCrosshairsQuestion, - mdiDoorbell, - mdiEyeCheck, - mdiFan, - mdiFanOff, - mdiGestureTapButton, - mdiLanConnect, - mdiLanDisconnect, - mdiLock, - mdiLockAlert, - mdiLockClock, - mdiLockOpen, - mdiMeterGas, - mdiMotionSensor, - mdiPackage, - mdiPackageDown, - mdiPackageUp, - mdiPipeValve, - mdiPowerPlug, - mdiPowerPlugOff, - mdiRestart, - mdiRobot, - mdiRobotConfused, - mdiRobotOff, - mdiSpeaker, - mdiSpeakerOff, - mdiSpeakerPause, - mdiSpeakerPlay, - mdiSwapHorizontal, - mdiTelevision, - mdiTelevisionOff, - mdiTelevisionPause, - mdiTelevisionPlay, - mdiToggleSwitchVariant, - mdiToggleSwitchVariantOff, - mdiVideo, - mdiVideoOff, - mdiWaterBoiler, - mdiWaterBoilerOff, - mdiWeatherNight, - mdiWhiteBalanceSunny, -} from "@mdi/js"; -import { HassEntity } from "home-assistant-js-websocket"; -import { UpdateEntity, updateIsInstalling } from "../../data/update"; -import { weatherIcon } from "../../data/weather"; -/** - * Return the icon to be used for a domain. - * - * Optionally pass in a state to influence the domain icon. - */ -import { DEFAULT_DOMAIN_ICON, FIXED_DOMAIN_ICONS } from "../const"; -import { alarmPanelIcon } from "./alarm_panel_icon"; -import { binarySensorIcon } from "./binary_sensor_icon"; -import { coverIcon } from "./cover_icon"; -import { numberIcon } from "./number_icon"; -import { sensorIcon } from "./sensor_icon"; - -export const domainIcon = ( - domain: string, - stateObj?: HassEntity, - state?: string -): string => { - const icon = domainIconWithoutDefault(domain, stateObj, state); - if (icon) { - return icon; - } - // eslint-disable-next-line - console.warn(`Unable to find icon for domain ${domain}`); - return DEFAULT_DOMAIN_ICON; -}; - -export const domainIconWithoutDefault = ( - domain: string, - stateObj?: HassEntity, - state?: string -): string | undefined => { - const compareState = state !== undefined ? state : stateObj?.state; - - switch (domain) { - case "alarm_control_panel": - return alarmPanelIcon(compareState); - - case "automation": - return compareState === "unavailable" - ? mdiRobotConfused - : compareState === "off" - ? mdiRobotOff - : mdiRobot; - - case "binary_sensor": - return binarySensorIcon(compareState, stateObj); - - case "button": - switch (stateObj?.attributes.device_class) { - case "identify": - return mdiCrosshairsQuestion; - case "restart": - return mdiRestart; - case "update": - return mdiPackageUp; - default: - return mdiButtonPointer; - } - - case "camera": - return compareState === "off" ? mdiVideoOff : mdiVideo; - - case "cover": - return coverIcon(compareState, stateObj); - - case "device_tracker": - if (stateObj?.attributes.source_type === "router") { - return compareState === "home" ? mdiLanConnect : mdiLanDisconnect; - } - if ( - ["bluetooth", "bluetooth_le"].includes(stateObj?.attributes.source_type) - ) { - return compareState === "home" ? mdiBluetoothConnect : mdiBluetooth; - } - return compareState === "not_home" ? mdiAccountArrowRight : mdiAccount; - - case "event": - switch (stateObj?.attributes.device_class) { - case "doorbell": - return mdiDoorbell; - case "button": - return mdiGestureTapButton; - case "motion": - return mdiMotionSensor; - default: - return mdiEyeCheck; - } - - case "fan": - return compareState === "off" ? mdiFanOff : mdiFan; - - case "humidifier": - return compareState === "off" ? mdiAirHumidifierOff : mdiAirHumidifier; - - case "input_boolean": - return compareState === "on" - ? mdiCheckCircleOutline - : mdiCloseCircleOutline; - - case "input_datetime": - if (!stateObj?.attributes.has_date) { - return mdiClock; - } - if (!stateObj.attributes.has_time) { - return mdiCalendar; - } - break; - - case "lock": - switch (compareState) { - case "unlocked": - return mdiLockOpen; - case "jammed": - return mdiLockAlert; - case "locking": - case "unlocking": - return mdiLockClock; - default: - return mdiLock; - } - - case "media_player": - switch (stateObj?.attributes.device_class) { - case "speaker": - switch (compareState) { - case "playing": - return mdiSpeakerPlay; - case "paused": - return mdiSpeakerPause; - case "off": - return mdiSpeakerOff; - default: - return mdiSpeaker; - } - case "tv": - switch (compareState) { - case "playing": - return mdiTelevisionPlay; - case "paused": - return mdiTelevisionPause; - case "off": - return mdiTelevisionOff; - default: - return mdiTelevision; - } - case "receiver": - switch (compareState) { - case "off": - return mdiAudioVideoOff; - default: - return mdiAudioVideo; - } - default: - switch (compareState) { - case "playing": - case "paused": - return mdiCastConnected; - case "off": - return mdiCastOff; - default: - return mdiCast; - } - } - - case "number": { - const icon = numberIcon(stateObj); - if (icon) { - return icon; - } - - break; - } - - case "person": - return compareState === "not_home" ? mdiAccountArrowRight : mdiAccount; - - case "switch": - switch (stateObj?.attributes.device_class) { - case "outlet": - return compareState === "on" ? mdiPowerPlug : mdiPowerPlugOff; - case "switch": - return compareState === "on" - ? mdiToggleSwitchVariant - : mdiToggleSwitchVariantOff; - default: - return mdiToggleSwitchVariant; - } - - case "sensor": { - const icon = sensorIcon(stateObj); - if (icon) { - return icon; - } - - break; - } - - case "sun": - return stateObj?.state === "above_horizon" - ? mdiWhiteBalanceSunny - : mdiWeatherNight; - - case "switch_as_x": - return mdiSwapHorizontal; - - case "threshold": - return mdiChartSankey; - - case "update": - return compareState === "on" - ? updateIsInstalling(stateObj as UpdateEntity) - ? mdiPackageDown - : mdiPackageUp - : mdiPackage; - - case "valve": - switch (stateObj?.attributes.device_class) { - case "water": - return mdiPipeValve; - case "gas": - return mdiMeterGas; - default: - return mdiPipeValve; - } - - case "water_heater": - return compareState === "off" ? mdiWaterBoilerOff : mdiWaterBoiler; - - case "weather": - return weatherIcon(stateObj?.state); - } - - if (domain in FIXED_DOMAIN_ICONS) { - return FIXED_DOMAIN_ICONS[domain]; - } - - return undefined; -}; diff --git a/src/common/entity/number_icon.ts b/src/common/entity/number_icon.ts deleted file mode 100644 index c468be6fed56..000000000000 --- a/src/common/entity/number_icon.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** Return an icon representing a number state. */ -import { HassEntity } from "home-assistant-js-websocket"; -import { FIXED_DEVICE_CLASS_ICONS } from "../const"; - -export const numberIcon = (stateObj?: HassEntity): string | undefined => { - const dclass = stateObj?.attributes.device_class; - - if (dclass && dclass in FIXED_DEVICE_CLASS_ICONS) { - return FIXED_DEVICE_CLASS_ICONS[dclass]; - } - - return undefined; -}; diff --git a/src/common/entity/sensor_icon.ts b/src/common/entity/sensor_icon.ts deleted file mode 100644 index 9af29ceb3e7d..000000000000 --- a/src/common/entity/sensor_icon.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** Return an icon representing a sensor state. */ -import { mdiBattery, mdiThermometer } from "@mdi/js"; -import { HassEntity } from "home-assistant-js-websocket"; -import { SENSOR_DEVICE_CLASS_BATTERY } from "../../data/sensor"; -import { FIXED_DEVICE_CLASS_ICONS, UNIT_C, UNIT_F } from "../const"; -import { batteryStateIcon } from "./battery_icon"; - -export const sensorIcon = (stateObj?: HassEntity): string | undefined => { - const dclass = stateObj?.attributes.device_class; - - if (dclass && dclass in FIXED_DEVICE_CLASS_ICONS) { - return FIXED_DEVICE_CLASS_ICONS[dclass]; - } - - if (dclass === SENSOR_DEVICE_CLASS_BATTERY) { - return stateObj ? batteryStateIcon(stateObj) : mdiBattery; - } - - const unit = stateObj?.attributes.unit_of_measurement; - if (unit === UNIT_C || unit === UNIT_F) { - return mdiThermometer; - } - - return undefined; -}; diff --git a/src/common/entity/state_icon.ts b/src/common/entity/state_icon.ts new file mode 100644 index 000000000000..b26f4e3267d7 --- /dev/null +++ b/src/common/entity/state_icon.ts @@ -0,0 +1,42 @@ +import { HassEntity } from "home-assistant-js-websocket"; +import { computeStateDomain } from "./compute_state_domain"; +import { updateIcon } from "./update_icon"; +import { deviceTrackerIcon } from "./device_tracker_icon"; +import { batteryIcon } from "./battery_icon"; + +export const stateIcon = ( + stateObj: HassEntity, + state?: string +): string | undefined => { + const domain = computeStateDomain(stateObj); + const compareState = state ?? stateObj.state; + const dc = stateObj.attributes.device_class; + switch (domain) { + case "update": + return updateIcon(stateObj, compareState); + + case "sensor": + if (dc === "battery") { + return batteryIcon(stateObj, compareState); + } + break; + + case "device_tracker": + return deviceTrackerIcon(stateObj, compareState); + + case "sun": + return compareState === "above_horizon" + ? "mdi:white-balance-sunny" + : "mdi:weather-night"; + + case "input_datetime": + if (!stateObj.attributes.has_date) { + return "mdi:clock"; + } + if (!stateObj.attributes.has_time) { + return "mdi:calendar"; + } + break; + } + return undefined; +}; diff --git a/src/common/entity/state_icon_path.ts b/src/common/entity/state_icon_path.ts deleted file mode 100644 index ac8e2c47ad46..000000000000 --- a/src/common/entity/state_icon_path.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** Return an icon representing a state. */ -import { HassEntity } from "home-assistant-js-websocket"; -import { DEFAULT_DOMAIN_ICON } from "../const"; -import { computeDomain } from "./compute_domain"; -import { domainIcon } from "./domain_icon"; - -export const stateIconPath = (state: HassEntity | undefined) => { - if (!state) { - return DEFAULT_DOMAIN_ICON; - } - return domainIcon(computeDomain(state.entity_id), state); -}; diff --git a/src/common/entity/update_icon.ts b/src/common/entity/update_icon.ts new file mode 100644 index 000000000000..4e1915d7fcad --- /dev/null +++ b/src/common/entity/update_icon.ts @@ -0,0 +1,11 @@ +import { HassEntity } from "home-assistant-js-websocket"; +import { UpdateEntity, updateIsInstalling } from "../../data/update"; + +export const updateIcon = (stateObj: HassEntity, state?: string) => { + const compareState = state ?? stateObj.state; + return compareState === "on" + ? updateIsInstalling(stateObj as UpdateEntity) + ? "mdi:package-down" + : "mdi:package-up" + : "mdi:package"; +}; diff --git a/src/components/data-table/ha-data-table.ts b/src/components/data-table/ha-data-table.ts index 44c49ea1090e..7f3d2f61b89b 100644 --- a/src/components/data-table/ha-data-table.ts +++ b/src/components/data-table/ha-data-table.ts @@ -764,10 +764,12 @@ export class HaDataTable extends LitElement { text-align: right; } - .mdc-data-table__cell--icon:first-child ha-icon, .mdc-data-table__cell--icon:first-child img, + .mdc-data-table__cell--icon:first-child ha-icon, + .mdc-data-table__cell--icon:first-child ha-svg-icon, .mdc-data-table__cell--icon:first-child ha-state-icon, - .mdc-data-table__cell--icon:first-child ha-svg-icon { + .mdc-data-table__cell--icon:first-child ha-domain-icon, + .mdc-data-table__cell--icon:first-child ha-service-icon { margin-left: 8px; } :host([dir="rtl"]) .mdc-data-table__cell--icon:first-child ha-icon, diff --git a/src/components/entity/ha-battery-icon.ts b/src/components/entity/ha-battery-icon.ts index 814f9d332e43..41f6a464f507 100644 --- a/src/components/entity/ha-battery-icon.ts +++ b/src/components/entity/ha-battery-icon.ts @@ -1,22 +1,25 @@ -import { html, LitElement } from "lit"; +import { HassEntity } from "home-assistant-js-websocket"; +import { html, LitElement, nothing } from "lit"; import { customElement, property } from "lit/decorators"; -import { batteryStateIcon } from "../../common/entity/battery_icon"; -import "../ha-svg-icon"; +import { batteryLevelIcon } from "../../common/entity/battery_icon"; +import "../ha-icon"; @customElement("ha-battery-icon") export class HaBatteryIcon extends LitElement { - @property() public batteryStateObj; + @property({ attribute: false }) public batteryStateObj?: HassEntity; - @property() public batteryChargingStateObj; + @property({ attribute: false }) public batteryChargingStateObj?: HassEntity; protected render() { + if (!this.batteryStateObj) return nothing; + return html` - + > `; } } diff --git a/src/components/ha-domain-icon.ts b/src/components/ha-domain-icon.ts new file mode 100644 index 000000000000..15659cb8c2d6 --- /dev/null +++ b/src/components/ha-domain-icon.ts @@ -0,0 +1,84 @@ +import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import { customElement, property } from "lit/decorators"; +import { until } from "lit/directives/until"; +import { DEFAULT_DOMAIN_ICON, FIXED_DOMAIN_ICONS } from "../common/const"; +import { domainIcon } from "../data/icons"; +import { HomeAssistant } from "../types"; +import { brandsUrl } from "../util/brands-url"; +import "./ha-icon"; + +@customElement("ha-domain-icon") +export class HaDomainIcon extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public domain?: string; + + @property() public deviceClass?: string; + + @property() public icon?: string; + + @property({ type: Boolean }) public brandFallback?: boolean; + + protected render() { + if (this.icon) { + return html``; + } + + if (!this.domain) { + return nothing; + } + + if (!this.hass) { + return this._renderFallback(); + } + + const icon = domainIcon(this.hass, this.domain, this.deviceClass).then( + (icn) => { + if (icn) { + return html``; + } + return this._renderFallback(); + } + ); + + return html`${until(icon)}`; + } + + private _renderFallback() { + if (this.domain! in FIXED_DOMAIN_ICONS) { + return html` + + `; + } + if (this.brandFallback) { + const image = brandsUrl({ + domain: this.domain!, + type: "icon", + darkOptimized: this.hass.themes?.darkMode, + }); + return html` + + `; + } + return html``; + } + + static get styles(): CSSResultGroup { + return css` + img { + width: var(--mdc-icon-size, 24px); + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-domain-icon": HaDomainIcon; + } +} diff --git a/src/components/ha-service-icon.ts b/src/components/ha-service-icon.ts index 780d3b81ac90..663043639edb 100644 --- a/src/components/ha-service-icon.ts +++ b/src/components/ha-service-icon.ts @@ -1,9 +1,8 @@ -import { mdiRoomService } from "@mdi/js"; import { html, LitElement, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import { until } from "lit/directives/until"; +import { DEFAULT_SERVICE_ICON, FIXED_DOMAIN_ICONS } from "../common/const"; import { computeDomain } from "../common/entity/compute_domain"; -import { domainIconWithoutDefault } from "../common/entity/domain_icon"; import { serviceIcon } from "../data/icons"; import { HomeAssistant } from "../types"; import "./ha-icon"; @@ -41,10 +40,11 @@ export class HaServiceIcon extends LitElement { } private _renderFallback() { + const domain = computeDomain(this.service!); + return html` `; } diff --git a/src/components/ha-state-icon.ts b/src/components/ha-state-icon.ts index 2589dc318f81..aa6371ec706b 100644 --- a/src/components/ha-state-icon.ts +++ b/src/components/ha-state-icon.ts @@ -2,7 +2,8 @@ import { HassEntity } from "home-assistant-js-websocket"; import { html, LitElement, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import { until } from "lit/directives/until"; -import { stateIconPath } from "../common/entity/state_icon_path"; +import { DEFAULT_DOMAIN_ICON, FIXED_DOMAIN_ICONS } from "../common/const"; +import { computeStateDomain } from "../common/entity/compute_state_domain"; import { entityIcon } from "../data/icons"; import { HomeAssistant } from "../types"; import "./ha-icon"; @@ -44,9 +45,13 @@ export class HaStateIcon extends LitElement { } private _renderFallback() { - return html``; + const domain = computeStateDomain(this.stateObj!); + + return html` + + `; } } diff --git a/src/components/trace/hat-script-graph.ts b/src/components/trace/hat-script-graph.ts index db2f800888e5..29aad8056436 100644 --- a/src/components/trace/hat-script-graph.ts +++ b/src/components/trace/hat-script-graph.ts @@ -17,10 +17,11 @@ import { mdiRoomService, mdiShuffleDisabled, } from "@mdi/js"; -import { css, html, LitElement, PropertyValues } from "lit"; +import { LitElement, PropertyValues, css, html } from "lit"; import { customElement, property } from "lit/decorators"; -import { fireEvent } from "../../common/dom/fire_event"; import { ensureArray } from "../../common/array/ensure-array"; +import { fireEvent } from "../../common/dom/fire_event"; +import { ACTION_ICONS } from "../../data/action"; import { Condition, Trigger } from "../../data/automation"; import { Action, @@ -45,7 +46,6 @@ import "./hat-graph-branch"; import { BRANCH_HEIGHT, NODE_SIZE, SPACING } from "./hat-graph-const"; import "./hat-graph-node"; import "./hat-graph-spacer"; -import { ACTION_ICONS } from "../../data/action"; export interface NodeInfo { path: string; diff --git a/src/data/icons.ts b/src/data/icons.ts index 5e8f4acae7cb..fc7452964eca 100644 --- a/src/data/icons.ts +++ b/src/data/icons.ts @@ -2,6 +2,7 @@ import { HassEntity } from "home-assistant-js-websocket"; import { computeDomain } from "../common/entity/compute_domain"; import { computeObjectId } from "../common/entity/compute_object_id"; import { computeStateDomain } from "../common/entity/compute_state_domain"; +import { stateIcon } from "../common/entity/state_icon"; import { HomeAssistant } from "../types"; import { EntityRegistryDisplayEntry, @@ -49,7 +50,7 @@ interface ComponentIcons { } interface ServiceIcons { - [domain: string]: Record; + [service: string]: string; } export type IconCategory = "entity" | "entity_component" | "services"; @@ -127,26 +128,18 @@ export const getServiceIcons = async ( export const entityIcon = async ( hass: HomeAssistant, - state: HassEntity, - stateValue?: string + stateObj: HassEntity, + state?: string ) => { - const entity = hass.entities?.[state.entity_id] as + const entry = hass.entities?.[stateObj.entity_id] as | EntityRegistryDisplayEntry | undefined; - if (entity?.icon) { - return entity.icon; + if (entry?.icon) { + return entry.icon; } - const domain = computeStateDomain(state); - const deviceClass = state.attributes.device_class; + const domain = computeStateDomain(stateObj); - return getEntityIcon( - hass, - domain, - deviceClass, - stateValue ?? state.state, - entity?.platform, - entity?.translation_key - ); + return getEntityIcon(hass, domain, stateObj, state, entry); }; export const entryIcon = async ( @@ -157,39 +150,41 @@ export const entryIcon = async ( return entry.icon; } const domain = computeDomain(entry.entity_id); - return getEntityIcon( - hass, - domain, - undefined, - undefined, - entry.platform, - entry.translation_key - ); + return getEntityIcon(hass, domain, undefined, undefined, entry); }; const getEntityIcon = async ( hass: HomeAssistant, domain: string, - deviceClass?: string, - value?: string, - platform?: string, - translation_key?: string + stateObj?: HassEntity, + stateValue?: string, + entry?: EntityRegistryEntry | EntityRegistryDisplayEntry ) => { + const platform = entry?.platform; + const translation_key = entry?.translation_key; + const device_class = stateObj?.attributes.device_class; + const state = stateValue ?? stateObj?.state; + let icon: string | undefined; if (translation_key && platform) { const platformIcons = await getPlatformIcons(hass, platform); if (platformIcons) { const translations = platformIcons[domain]?.[translation_key]; - icon = (value && translations?.state?.[value]) || translations?.default; + icon = (state && translations?.state?.[state]) || translations?.default; } } + + if (!icon && stateObj) { + icon = stateIcon(stateObj, state); + } + if (!icon) { const entityComponentIcons = await getComponentIcons(hass, domain); if (entityComponentIcons) { const translations = - (deviceClass && entityComponentIcons[deviceClass]) || + (device_class && entityComponentIcons[device_class]) || entityComponentIcons._; - icon = (value && translations?.state?.[value]) || translations?.default; + icon = (state && translations?.state?.[state]) || translations?.default; } } return icon; @@ -234,12 +229,34 @@ export const attributeIcon = async ( return icon; }; -export const serviceIcon = async (hass: HomeAssistant, service: string) => { +export const serviceIcon = async ( + hass: HomeAssistant, + service: string +): Promise => { + let icon: string | undefined; const domain = computeDomain(service); const serviceName = computeObjectId(service); const serviceIcons = await getServiceIcons(hass, domain); if (serviceIcons) { - return serviceIcons[serviceName]; + icon = serviceIcons[serviceName]; + } + if (!icon) { + icon = await domainIcon(hass, domain); + } + return icon; +}; + +export const domainIcon = async ( + hass: HomeAssistant, + domain: string, + deviceClass?: string +): Promise => { + const entityComponentIcons = await getComponentIcons(hass, domain); + if (entityComponentIcons) { + const translations = + (deviceClass && entityComponentIcons[deviceClass]) || + entityComponentIcons._; + return translations?.default; } return undefined; }; diff --git a/src/data/weather.ts b/src/data/weather.ts index abded90d6d3b..8f02b7c5f334 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -8,7 +8,6 @@ import { mdiWeatherLightning, mdiWeatherLightningRainy, mdiWeatherNight, - mdiWeatherNightPartlyCloudy, mdiWeatherPartlyCloudy, mdiWeatherPouring, mdiWeatherRainy, @@ -520,13 +519,6 @@ export const getWeatherStateIcon = ( return undefined; }; -export const weatherIcon = (state?: string, nightTime?: boolean): string => - !state - ? undefined - : nightTime && state === "partlycloudy" - ? mdiWeatherNightPartlyCloudy - : weatherIcons[state]; - const EIGHT_HOURS = 28800000; const DAY_IN_MILLISECONDS = 86400000; diff --git a/src/dialogs/quick-bar/ha-quick-bar.ts b/src/dialogs/quick-bar/ha-quick-bar.ts index 71da2ec0dcb6..da30fb7e9b4b 100644 --- a/src/dialogs/quick-bar/ha-quick-bar.ts +++ b/src/dialogs/quick-bar/ha-quick-bar.ts @@ -8,7 +8,7 @@ import { mdiReload, mdiServerNetwork, } from "@mdi/js"; -import { LitElement, css, html, nothing } from "lit"; +import { LitElement, TemplateResult, css, html, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; import { styleMap } from "lit/directives/style-map"; @@ -17,9 +17,7 @@ import { canShowPage } from "../../common/config/can_show_page"; import { componentsWithService } from "../../common/config/components_with_service"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { fireEvent } from "../../common/dom/fire_event"; -import { computeDomain } from "../../common/entity/compute_domain"; import { computeStateName } from "../../common/entity/compute_state_name"; -import { domainIcon } from "../../common/entity/domain_icon"; import { navigate } from "../../common/navigate"; import { caseInsensitiveStringCompare } from "../../common/string/compare"; import { @@ -27,9 +25,9 @@ import { fuzzyFilterSort, } from "../../common/string/filter/sequence-matching"; import { debounce } from "../../common/util/debounce"; -import "../../components/ha-label"; import "../../components/ha-circular-progress"; import "../../components/ha-icon-button"; +import "../../components/ha-label"; import "../../components/ha-list-item"; import "../../components/ha-textfield"; import { fetchHassioAddonsInfo } from "../../data/hassio/addon"; @@ -56,7 +54,7 @@ interface CommandItem extends QuickBarItem { interface EntityItem extends QuickBarItem { altText: string; - icon?: string; + icon?: TemplateResult; } const isCommandItem = (item: QuickBarItem): item is CommandItem => @@ -296,16 +294,14 @@ export class QuickBar extends LitElement { graphic="icon" > ${item.iconPath - ? html`` - : html``} + ? html` + + ` + : html`${item.icon}`} ${item.primaryText} ${item.altText ? html` @@ -476,10 +472,12 @@ export class QuickBar extends LitElement { const entityItem = { primaryText: computeStateName(entityState), altText: entityId, - icon: entityState.attributes.icon, - iconPath: entityState.attributes.icon - ? undefined - : domainIcon(computeDomain(entityId), entityState), + icon: html` + + `, action: () => fireEvent(this, "hass-more-info", { entityId }), }; diff --git a/src/panels/config/automation/add-automation-element-dialog.ts b/src/panels/config/automation/add-automation-element-dialog.ts index bd9a7681abef..4145a3397b50 100644 --- a/src/panels/config/automation/add-automation-element-dialog.ts +++ b/src/panels/config/automation/add-automation-element-dialog.ts @@ -1,4 +1,5 @@ import "@material/mwc-list/mwc-list"; +import "@material/web/divider/divider"; import { mdiClose, mdiContentPaste, mdiPlus } from "@mdi/js"; import Fuse, { IFuseOptions } from "fuse.js"; import { @@ -16,17 +17,21 @@ import { repeat } from "lit/directives/repeat"; import { styleMap } from "lit/directives/style-map"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../common/dom/fire_event"; -import { domainIconWithoutDefault } from "../../../common/entity/domain_icon"; +import { computeDomain } from "../../../common/entity/compute_domain"; import { stringCompare } from "../../../common/string/compare"; import { LocalizeFunc } from "../../../common/translations/localize"; +import { deepEqual } from "../../../common/util/deep-equal"; import "../../../components/ha-dialog"; import type { HaDialog } from "../../../components/ha-dialog"; import "../../../components/ha-dialog-header"; +import "../../../components/ha-domain-icon"; import "../../../components/ha-icon-button"; import "../../../components/ha-icon-button-prev"; import "../../../components/ha-icon-next"; -import "../../../components/ha-list-new"; import "../../../components/ha-list-item-new"; +import "../../../components/ha-list-new"; +import "../../../components/ha-service-icon"; +import "../../../components/search-input"; import { ACTION_GROUPS, ACTION_ICONS, @@ -36,6 +41,7 @@ import { } from "../../../data/action"; import { AutomationElementGroup } from "../../../data/automation"; import { CONDITION_GROUPS, CONDITION_ICONS } from "../../../data/condition"; +import { getServiceIcons } from "../../../data/icons"; import { IntegrationManifest, domainToName, @@ -45,16 +51,10 @@ import { TRIGGER_GROUPS, TRIGGER_ICONS } from "../../../data/trigger"; import { HassDialog } from "../../../dialogs/make-dialog-manager"; import { haStyle, haStyleDialog } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; -import { brandsUrl } from "../../../util/brands-url"; import { AddAutomationElementDialogParams, PASTE_VALUE, } from "./show-add-automation-element-dialog"; -import { computeDomain } from "../../../common/entity/compute_domain"; -import { deepEqual } from "../../../common/util/deep-equal"; -import "../../../components/search-input"; -import "@material/web/divider/divider"; -import { getServiceIcons } from "../../../data/icons"; const TYPES = { trigger: { groups: TRIGGER_GROUPS, icons: TRIGGER_ICONS }, @@ -74,7 +74,6 @@ interface ListItem { description: string; iconPath?: string; icon?: TemplateResult; - image?: string; group: boolean; } @@ -318,17 +317,15 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { (!domainUsed && manifest?.integration_type === "entity") || !["helper", "entity"].includes(manifest?.integration_type || ""))) ) { - const icon = domainIconWithoutDefault(domain); result.push({ group: true, - iconPath: icon, - image: !icon - ? brandsUrl({ - domain, - type: "icon", - darkOptimized: this.hass.themes?.darkMode, - }) - : undefined, + icon: html` + + `, key: `${SERVICE_PREFIX}${domain}`, name: domainToName(localize, domain, manifest), description: "", @@ -364,10 +361,12 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { for (const service of services_keys) { result.push({ group: false, - icon: html``, + icon: html` + + `, key: `${SERVICE_PREFIX}${dmn}.${service}`, name: `${domain ? "" : `${domainToName(localize, dmn)}: `}${ this.hass.localize(`component.${dmn}.services.${service}.name`) || @@ -578,13 +577,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { slot="start" .path=${item.iconPath} >` - : html``} + : nothing} ${item.group ? html`` : html` - - +
diff --git a/src/panels/lovelace/cards/hui-area-card.ts b/src/panels/lovelace/cards/hui-area-card.ts index fd8f89072263..a203f8228bc8 100644 --- a/src/panels/lovelace/cards/hui-area-card.ts +++ b/src/panels/lovelace/cards/hui-area-card.ts @@ -23,9 +23,8 @@ import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { styleMap } from "lit/directives/style-map"; import memoizeOne from "memoize-one"; -import { FIXED_DEVICE_CLASS_ICONS, STATES_OFF } from "../../../common/const"; +import { STATES_OFF } from "../../../common/const"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; -import { binarySensorIcon } from "../../../common/entity/binary_sensor_icon"; import { computeDomain } from "../../../common/entity/compute_domain"; import { navigate } from "../../../common/navigate"; import { @@ -36,8 +35,9 @@ import { blankBeforeUnit } from "../../../common/translations/blank_before_unit" import parseAspectRatio from "../../../common/util/parse-aspect-ratio"; import { subscribeOne } from "../../../common/util/subscribe-one"; import "../../../components/ha-card"; +import "../../../components/ha-domain-icon"; import "../../../components/ha-icon-button"; -import "../../../components/ha-svg-icon"; +import "../../../components/ha-state-icon"; import { AreaRegistryEntry, subscribeAreaRegistry, @@ -388,13 +388,16 @@ export class HuiAreaCard (entity) => entity.attributes.device_class === deviceClass ) ) { - const icon = FIXED_DEVICE_CLASS_ICONS[deviceClass]; - sensors.push( - html`
- ${icon ? html`` : ""} + sensors.push(html` +
+ ${this._average(domain, deviceClass)} -
` - ); +
+ `); } }); }); @@ -434,16 +437,18 @@ export class HuiAreaCard
${ALERT_DOMAINS.map((domain) => { if (!(domain in entitiesByDomain)) { - return ""; + return nothing; } return this._deviceClasses[domain].map((deviceClass) => { const entity = this._isOn(domain, deviceClass); return entity - ? html`` + ? html` + + ` : nothing; }); })} @@ -572,7 +577,14 @@ export class HuiAreaCard padding: 16px; } - .alerts ha-svg-icon { + ha-state-icon { + display: inline-flex; + align-items: center; + justify-content: center; + position: relative; + } + + .alerts ha-state-icon { background: var(--accent-color); color: var(--text-accent-color, var(--text-primary-color)); padding: 8px; diff --git a/src/panels/lovelace/cards/hui-plant-status-card.ts b/src/panels/lovelace/cards/hui-plant-status-card.ts index 74c080031ae5..80435b1f0b74 100644 --- a/src/panels/lovelace/cards/hui-plant-status-card.ts +++ b/src/panels/lovelace/cards/hui-plant-status-card.ts @@ -16,7 +16,7 @@ import { import { customElement, property, state } from "lit/decorators"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { fireEvent } from "../../../common/dom/fire_event"; -import { batteryIcon } from "../../../common/entity/battery_icon"; +import { batteryLevelIcon } from "../../../common/entity/battery_icon"; import { computeStateName } from "../../../common/entity/compute_state_name"; import "../../../components/ha-card"; import "../../../components/ha-svg-icon"; @@ -252,7 +252,7 @@ class HuiPlantStatusCard extends LitElement implements LovelaceCard { private computeIcon(attr: string, batLvl: number): string { if (attr === "battery") { - return batteryIcon(batLvl); + return batteryLevelIcon(batLvl); } return SENSOR_ICONS[attr]; } diff --git a/src/panels/media-browser/ha-bar-media-player.ts b/src/panels/media-browser/ha-bar-media-player.ts index f9cbb286893a..eb459f5ba8d3 100644 --- a/src/panels/media-browser/ha-bar-media-player.ts +++ b/src/panels/media-browser/ha-bar-media-player.ts @@ -1,7 +1,5 @@ -import "@material/mwc-button/mwc-button"; import "@material/mwc-linear-progress/mwc-linear-progress"; import type { LinearProgress } from "@material/mwc-linear-progress/mwc-linear-progress"; -import "@material/mwc-list/mwc-list-item"; import { mdiChevronDown, mdiMonitor, @@ -12,11 +10,11 @@ import { mdiVolumeHigh, } from "@mdi/js"; import { - css, CSSResultGroup, - html, LitElement, PropertyValues, + css, + html, nothing, } from "lit"; import { customElement, property, query, state } from "lit/decorators"; @@ -26,25 +24,29 @@ import { fireEvent } from "../../common/dom/fire_event"; import { computeDomain } from "../../common/entity/compute_domain"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { computeStateName } from "../../common/entity/compute_state_name"; -import { domainIcon } from "../../common/entity/domain_icon"; import { supportsFeature } from "../../common/entity/supports-feature"; +import { debounce } from "../../common/util/debounce"; import "../../components/ha-button"; import "../../components/ha-button-menu"; import "../../components/ha-circular-progress"; +import "../../components/ha-domain-icon"; import "../../components/ha-icon-button"; +import "../../components/ha-list-item"; +import "../../components/ha-state-icon"; +import "../../components/ha-svg-icon"; import { UNAVAILABLE } from "../../data/entity"; import { BROWSER_PLAYER, + ControlButton, + MediaPlayerEntity, + MediaPlayerEntityFeature, + MediaPlayerItem, cleanupMediaTitle, computeMediaControls, computeMediaDescription, - ControlButton, formatMediaTime, getCurrentProgress, handleMediaControlClick, - MediaPlayerEntity, - MediaPlayerEntityFeature, - MediaPlayerItem, setMediaPlayerVolume, } from "../../data/media-player"; import { ResolvedMediaSource } from "../../data/media_source"; @@ -56,7 +58,6 @@ import { BrowserMediaPlayer, ERR_UNSUPPORTED_MEDIA, } from "./browser-media-player"; -import { debounce } from "../../common/util/debounce"; declare global { interface HASSDomEvents { @@ -320,22 +321,19 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) { : "" } - + ${ this.narrow ? html` - + + ${this._renderIcon(isBrowser, stateObj)} + ` : html` - + + ${this._renderIcon(isBrowser, stateObj)} + ` } - ${this.hass.localize("ui.components.media-browser.web-browser")} - + ${this._mediaPlayerEntities.map( (source) => html` - ${computeStateName(source)} - + ` )} @@ -382,6 +377,23 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) { `; } + private _renderIcon(isBrowser: boolean, stateObj?: MediaPlayerEntity) { + if (isBrowser) { + return html``; + } + if (stateObj) { + return html` + + `; + } + return html` + + `; + } + public willUpdate(changedProps: PropertyValues) { super.willUpdate(changedProps); if (changedProps.has("entityId")) { @@ -571,9 +583,10 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) { --mdc-theme-primary: var(--secondary-text-color); } - mwc-button[slot="trigger"] { + ha-button-menu ha-button[slot="trigger"] { + line-height: 1; --mdc-theme-primary: var(--primary-text-color); - --mdc-icon-size: 36px; + --mdc-icon-size: 16px; } .info { @@ -650,10 +663,6 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) { margin: 16px 0 16px 16px; } - ha-button-menu mwc-button { - line-height: 1; - } - :host([narrow]) { height: 57px; } @@ -696,10 +705,15 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) { left: 0; } - mwc-list-item[selected] { + ha-list-item[selected] { font-weight: bold; } + span[slot="icon"] { + display: flex; + align-items: center; + } + ha-svg-icon[slot="trailingIcon"] { margin-inline-start: 8px !important; margin-inline-end: 0px !important;