@@ -307,7 +308,7 @@ export class HcConnect extends LitElement {
color: darkred;
}
- mwc-button ha-icon {
+ mwc-button ha-svg-icon {
margin-left: 8px;
}
diff --git a/demo/src/custom-cards/cast-demo-row.ts b/demo/src/custom-cards/cast-demo-row.ts
index 8fbecb4eb5..e400031878 100644
--- a/demo/src/custom-cards/cast-demo-row.ts
+++ b/demo/src/custom-cards/cast-demo-row.ts
@@ -1,3 +1,4 @@
+import { mdiTelevision } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, state } from "lit/decorators";
import { CastManager } from "../../../src/cast/cast_manager";
@@ -27,7 +28,7 @@ class CastDemoRow extends LitElement implements LovelaceRow {
return html``;
}
return html`
-
Show Chromecast interface
@@ -72,7 +73,7 @@ class CastDemoRow extends LitElement implements LovelaceRow {
display: flex;
align-items: center;
}
- ha-icon {
+ ha-svg-icon {
padding: 8px;
color: var(--paper-item-icon-color);
}
diff --git a/src/common/const.ts b/src/common/const.ts
index fd2590bccd..6559ece953 100644
--- a/src/common/const.ts
+++ b/src/common/const.ts
@@ -1,88 +1,146 @@
/** Constants to be used in the frontend. */
+import {
+ mdiAccount,
+ mdiAirFilter,
+ mdiAlert,
+ mdiAngleAcute,
+ mdiAppleSafari,
+ mdiBell,
+ mdiBookmark,
+ mdiBrightness5,
+ mdiBullhorn,
+ mdiCalendar,
+ mdiCalendarClock,
+ mdiCash,
+ mdiClock,
+ mdiCloudUpload,
+ mdiCog,
+ mdiCommentAlert,
+ mdiCounter,
+ mdiCurrentAc,
+ mdiEye,
+ mdiFan,
+ mdiFlash,
+ mdiFlower,
+ mdiFormatListBulleted,
+ mdiFormTextbox,
+ mdiGasCylinder,
+ mdiGauge,
+ mdiGoogleAssistant,
+ mdiGoogleCirclesCommunities,
+ mdiHomeAssistant,
+ mdiHomeAutomation,
+ mdiImageFilterFrames,
+ mdiLightbulb,
+ mdiLightningBolt,
+ mdiMailbox,
+ mdiMapMarkerRadius,
+ mdiMolecule,
+ mdiMoleculeCo,
+ mdiMoleculeCo2,
+ mdiPalette,
+ mdiRayVertex,
+ mdiRemote,
+ mdiRobot,
+ mdiRobotVacuum,
+ mdiScriptText,
+ mdiSineWave,
+ mdiTextToSpeech,
+ mdiThermometer,
+ mdiThermostat,
+ mdiTimerOutline,
+ mdiToggleSwitchOutline,
+ mdiVideo,
+ mdiWaterPercent,
+ mdiWeatherCloudy,
+ mdiWhiteBalanceSunny,
+ mdiWifi,
+} from "@mdi/js";
+
// Constants should be alphabetically sorted by name.
// 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 domain. */
-export const DEFAULT_DOMAIN_ICON = "hass:bookmark";
+export const DEFAULT_DOMAIN_ICON = mdiBookmark;
/** 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",
- number: "hass:ray-vertex",
- persistent_notification: "hass:bell",
- person: "hass:account",
- plant: "hass:flower",
- proximity: "hass:apple-safari",
- remote: "hass:remote",
- scene: "hass:palette",
- script: "hass:script-text",
- select: "hass:format-list-bulleted",
- 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",
+ alert: mdiAlert,
+ air_quality: mdiAirFilter,
+ automation: mdiRobot,
+ calendar: mdiCalendar,
+ camera: mdiVideo,
+ climate: mdiThermostat,
+ configurator: mdiCog,
+ conversation: mdiTextToSpeech,
+ counter: mdiCounter,
+ device_tracker: mdiAccount,
+ fan: mdiFan,
+ google_assistant: mdiGoogleAssistant,
+ group: mdiGoogleCirclesCommunities,
+ homeassistant: mdiHomeAssistant,
+ homekit: mdiHomeAutomation,
+ image_processing: mdiImageFilterFrames,
+ input_boolean: mdiToggleSwitchOutline,
+ input_datetime: mdiCalendarClock,
+ input_number: mdiRayVertex,
+ input_select: mdiFormatListBulleted,
+ input_text: mdiFormTextbox,
+ light: mdiLightbulb,
+ mailbox: mdiMailbox,
+ notify: mdiCommentAlert,
+ number: mdiRayVertex,
+ persistent_notification: mdiBell,
+ person: mdiAccount,
+ plant: mdiFlower,
+ proximity: mdiAppleSafari,
+ remote: mdiRemote,
+ scene: mdiPalette,
+ script: mdiScriptText,
+ select: mdiFormatListBulleted,
+ sensor: mdiEye,
+ siren: mdiBullhorn,
+ simple_alarm: mdiBell,
+ sun: mdiWhiteBalanceSunny,
+ switch: mdiFlash,
+ timer: mdiTimerOutline,
+ updater: mdiCloudUpload,
+ vacuum: mdiRobotVacuum,
+ water_heater: mdiThermometer,
+ weather: mdiWeatherCloudy,
+ zone: mdiMapMarkerRadius,
};
export const FIXED_DEVICE_CLASS_ICONS = {
- aqi: "hass:air-filter",
- // battery: "hass:battery", => not included by design since `sensorIcon()` will dynamically determine the icon
- carbon_dioxide: "mdi:molecule-co2",
- carbon_monoxide: "mdi:molecule-co",
- current: "hass:current-ac",
- date: "hass:calendar",
- energy: "hass:lightning-bolt",
- gas: "hass:gas-cylinder",
- humidity: "hass:water-percent",
- illuminance: "hass:brightness-5",
- monetary: "mdi:cash",
- nitrogen_dioxide: "mdi:molecule",
- nitrogen_monoxide: "mdi:molecule",
- nitrous_oxide: "mdi:molecule",
- ozone: "mdi:molecule",
- pm1: "mdi:molecule",
- pm10: "mdi:molecule",
- pm25: "mdi:molecule",
- power: "hass:flash",
- power_factor: "hass:angle-acute",
- pressure: "hass:gauge",
- signal_strength: "hass:wifi",
- sulphur_dioxide: "mdi:molecule",
- temperature: "hass:thermometer",
- timestamp: "hass:clock",
- volatile_organic_compounds: "mdi:molecule",
- voltage: "hass:sine-wave",
+ aqi: mdiAirFilter,
+ // battery: mdiBattery, => not included by design since `sensorIcon()` will dynamically determine the icon
+ carbon_dioxide: mdiMoleculeCo2,
+ carbon_monoxide: mdiMoleculeCo,
+ current: mdiCurrentAc,
+ date: mdiCalendar,
+ energy: mdiLightningBolt,
+ gas: mdiGasCylinder,
+ humidity: mdiWaterPercent,
+ illuminance: mdiBrightness5,
+ monetary: mdiCash,
+ nitrogen_dioxide: mdiMolecule,
+ nitrogen_monoxide: mdiMolecule,
+ nitrous_oxide: mdiMolecule,
+ ozone: mdiMolecule,
+ pm1: mdiMolecule,
+ pm10: mdiMolecule,
+ pm25: mdiMolecule,
+ power: mdiFlash,
+ power_factor: mdiAngleAcute,
+ pressure: mdiGauge,
+ signal_strength: mdiWifi,
+ sulphur_dioxide: mdiMolecule,
+ temperature: mdiThermometer,
+ timestamp: mdiClock,
+ volatile_organic_compounds: mdiMolecule,
+ voltage: mdiSineWave,
};
/** Domains that have a state card. */
diff --git a/src/common/entity/alarm_panel_icon.ts b/src/common/entity/alarm_panel_icon.ts
index aa2590ed67..cdbb736b3a 100644
--- a/src/common/entity/alarm_panel_icon.ts
+++ b/src/common/entity/alarm_panel_icon.ts
@@ -1,24 +1,36 @@
/** 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 "hass:shield-lock";
+ return mdiShieldLock;
case "armed_vacation":
- return "hass:shield-airplane";
+ return mdiShieldAirplane;
case "armed_home":
- return "hass:shield-home";
+ return mdiShieldHome;
case "armed_night":
- return "hass:shield-moon";
+ return mdiShieldMoon;
case "armed_custom_bypass":
- return "hass:security";
+ return mdiSecurity;
case "pending":
- return "hass:shield-outline";
+ return mdiShieldOutline;
case "triggered":
- return "hass:bell-ring";
+ return mdiBellRing;
case "disarmed":
- return "hass:shield-off";
+ return mdiShieldOff;
default:
- return "hass:shield";
+ return mdiShield;
}
};
diff --git a/src/common/entity/battery_icon.ts b/src/common/entity/battery_icon.ts
index f4b1784232..d73cdbe489 100644
--- a/src/common/entity/battery_icon.ts
+++ b/src/common/entity/battery_icon.ts
@@ -1,35 +1,92 @@
/** 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";
-export const batteryIcon = (
+const BATTERY_ICONS = {
+ 10: mdiBattery10,
+ 20: mdiBattery20,
+ 30: mdiBattery30,
+ 40: mdiBattery40,
+ 50: mdiBattery50,
+ 60: mdiBattery60,
+ 70: mdiBattery70,
+ 80: mdiBattery80,
+ 90: mdiBattery90,
+ 100: mdiBattery,
+};
+const BATTERY_CHARGING_ICONS = {
+ 10: mdiBatteryCharging10,
+ 20: mdiBatteryCharging20,
+ 30: mdiBatteryCharging30,
+ 40: mdiBatteryCharging40,
+ 50: mdiBatteryCharging50,
+ 60: mdiBatteryCharging60,
+ 70: mdiBatteryCharging70,
+ 80: mdiBatteryCharging80,
+ 90: mdiBatteryCharging90,
+ 100: mdiBatteryCharging,
+};
+
+export const batteryStateIcon = (
batteryState: HassEntity,
batteryChargingState?: HassEntity
) => {
- const battery = Number(batteryState.state);
- const battery_charging =
+ const battery = batteryState.state;
+ const batteryCharging =
batteryChargingState && batteryChargingState.state === "on";
- let icon = "hass:battery";
- if (isNaN(battery)) {
- if (batteryState.state === "off") {
- icon += "-full";
- } else if (batteryState.state === "on") {
- icon += "-alert";
- } else {
- icon += "-unknown";
- }
- return icon;
- }
-
- const batteryRound = Math.round(battery / 10) * 10;
- if (battery_charging && battery > 10) {
- icon += `-charging-${batteryRound}`;
- } else if (battery_charging) {
- icon += "-outline";
- } else if (battery <= 5) {
- icon += "-alert";
- } else if (battery > 5 && battery < 95) {
- icon += `-${batteryRound}`;
- }
- return icon;
+ return batteryIcon(battery, batteryCharging);
+};
+
+export const batteryIcon = (
+ batteryState: number | string,
+ batteryCharging?: boolean
+) => {
+ const batteryValue = Number(batteryState);
+ if (isNaN(batteryValue)) {
+ if (batteryState === "off") {
+ return mdiBattery;
+ }
+ if (batteryState === "on") {
+ return mdiBatteryAlert;
+ }
+ return mdiBatteryUnknown;
+ }
+
+ const batteryRound = Math.round(batteryValue / 10) * 10;
+ if (batteryCharging && batteryValue >= 10) {
+ return BATTERY_CHARGING_ICONS[batteryRound];
+ }
+ if (batteryCharging) {
+ return mdiBatteryChargingOutline;
+ }
+ if (batteryValue <= 5) {
+ return mdiBatteryAlertVariantOutline;
+ }
+ return BATTERY_ICONS[batteryRound];
};
diff --git a/src/common/entity/binary_sensor_icon.ts b/src/common/entity/binary_sensor_icon.ts
index 8ee9864e77..a4adec1e60 100644
--- a/src/common/entity/binary_sensor_icon.ts
+++ b/src/common/entity/binary_sensor_icon.ts
@@ -1,3 +1,44 @@
+import {
+ mdiBattery,
+ mdiBatteryOutline,
+ mdiBatteryCharging,
+ mdiThermometer,
+ mdiSnowflake,
+ mdiServerNetworkOff,
+ mdiServerNetwork,
+ mdiDoorClosed,
+ mdiDoorOpen,
+ mdiGarage,
+ mdiGarageOpen,
+ mdiPowerPlugOff,
+ mdiPowerPlug,
+ mdiCheckCircle,
+ mdiAlertCircle,
+ mdiSmoke,
+ mdiFire,
+ mdiBrightness5,
+ mdiBrightness7,
+ mdiLock,
+ mdiLockOpen,
+ mdiWaterOff,
+ mdiWater,
+ mdiWalk,
+ mdiRun,
+ mdiHomeOutline,
+ mdiHome,
+ mdiSquare,
+ mdiSquareOutline,
+ mdiMusicNoteOff,
+ mdiMusicNote,
+ mdiPackage,
+ mdiPackageUp,
+ mdiCropPortrait,
+ mdiVibrate,
+ mdiWindowClosed,
+ mdiWindowOpen,
+ mdiRadioboxBlank,
+ mdiCheckboxMarkedCircle,
+} from "@mdi/js";
import { HassEntity } from "home-assistant-js-websocket";
/** Return an icon representing a binary sensor state. */
@@ -6,53 +47,53 @@ 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";
+ return is_off ? mdiBattery : mdiBatteryOutline;
case "battery_charging":
- return is_off ? "hass:battery" : "hass:battery-charging";
+ return is_off ? mdiBattery : mdiBatteryCharging;
case "cold":
- return is_off ? "hass:thermometer" : "hass:snowflake";
+ return is_off ? mdiThermometer : mdiSnowflake;
case "connectivity":
- return is_off ? "hass:server-network-off" : "hass:server-network";
+ return is_off ? mdiServerNetworkOff : mdiServerNetwork;
case "door":
- return is_off ? "hass:door-closed" : "hass:door-open";
+ return is_off ? mdiDoorClosed : mdiDoorOpen;
case "garage_door":
- return is_off ? "hass:garage" : "hass:garage-open";
+ return is_off ? mdiGarage : mdiGarageOpen;
case "power":
- return is_off ? "hass:power-plug-off" : "hass:power-plug";
+ return is_off ? mdiPowerPlugOff : mdiPowerPlug;
case "gas":
case "problem":
case "safety":
case "tamper":
- return is_off ? "hass:check-circle" : "hass:alert-circle";
+ return is_off ? mdiCheckCircle : mdiAlertCircle;
case "smoke":
- return is_off ? "hass:check-circle" : "hass:smoke";
+ return is_off ? mdiCheckCircle : mdiSmoke;
case "heat":
- return is_off ? "hass:thermometer" : "hass:fire";
+ return is_off ? mdiThermometer : mdiFire;
case "light":
- return is_off ? "hass:brightness-5" : "hass:brightness-7";
+ return is_off ? mdiBrightness5 : mdiBrightness7;
case "lock":
- return is_off ? "hass:lock" : "hass:lock-open";
+ return is_off ? mdiLock : mdiLockOpen;
case "moisture":
- return is_off ? "hass:water-off" : "hass:water";
+ return is_off ? mdiWaterOff : mdiWater;
case "motion":
- return is_off ? "hass:walk" : "hass:run";
+ return is_off ? mdiWalk : mdiRun;
case "occupancy":
- return is_off ? "hass:home-outline" : "hass:home";
+ return is_off ? mdiHomeOutline : mdiHome;
case "opening":
- return is_off ? "hass:square" : "hass:square-outline";
+ return is_off ? mdiSquare : mdiSquareOutline;
case "plug":
- return is_off ? "hass:power-plug-off" : "hass:power-plug";
+ return is_off ? mdiPowerPlugOff : mdiPowerPlug;
case "presence":
- return is_off ? "hass:home-outline" : "hass:home";
+ return is_off ? mdiHomeOutline : mdiHome;
case "sound":
- return is_off ? "hass:music-note-off" : "hass:music-note";
+ return is_off ? mdiMusicNoteOff : mdiMusicNote;
case "update":
- return is_off ? "mdi:package" : "mdi:package-up";
+ return is_off ? mdiPackage : mdiPackageUp;
case "vibration":
- return is_off ? "hass:crop-portrait" : "hass:vibrate";
+ return is_off ? mdiCropPortrait : mdiVibrate;
case "window":
- return is_off ? "hass:window-closed" : "hass:window-open";
+ return is_off ? mdiWindowClosed : mdiWindowOpen;
default:
- return is_off ? "hass:radiobox-blank" : "hass:checkbox-marked-circle";
+ return is_off ? mdiRadioboxBlank : mdiCheckboxMarkedCircle;
}
};
diff --git a/src/common/entity/cover_icon.ts b/src/common/entity/cover_icon.ts
index 011f8e7294..4308306890 100644
--- a/src/common/entity/cover_icon.ts
+++ b/src/common/entity/cover_icon.ts
@@ -1,4 +1,30 @@
/** Return an icon representing a cover state. */
+import {
+ mdiArrowUpBox,
+ mdiArrowDownBox,
+ mdiGarage,
+ mdiGarageOpen,
+ mdiGateArrowRight,
+ mdiGate,
+ mdiGateOpen,
+ mdiDoorOpen,
+ mdiDoorClosed,
+ mdiCircle,
+ mdiWindowShutter,
+ mdiWindowShutterOpen,
+ mdiBlinds,
+ mdiBlindsOpen,
+ mdiWindowClosed,
+ mdiWindowOpen,
+ mdiArrowExpandHorizontal,
+ mdiArrowUp,
+ mdiArrowCollapseHorizontal,
+ mdiArrowDown,
+ mdiCircleSlice8,
+ mdiArrowSplitVertical,
+ mdiCurtains,
+ mdiCurtainsClosed,
+} from "@mdi/js";
import { HassEntity } from "home-assistant-js-websocket";
export const coverIcon = (state?: string, stateObj?: HassEntity): string => {
@@ -8,74 +34,84 @@ export const coverIcon = (state?: string, stateObj?: HassEntity): string => {
case "garage":
switch (state) {
case "opening":
- return "hass:arrow-up-box";
+ return mdiArrowUpBox;
case "closing":
- return "hass:arrow-down-box";
+ return mdiArrowDownBox;
case "closed":
- return "hass:garage";
+ return mdiGarage;
default:
- return "hass:garage-open";
+ return mdiGarageOpen;
}
case "gate":
switch (state) {
case "opening":
case "closing":
- return "hass:gate-arrow-right";
+ return mdiGateArrowRight;
case "closed":
- return "hass:gate";
+ return mdiGate;
default:
- return "hass:gate-open";
+ return mdiGateOpen;
}
case "door":
- return open ? "hass:door-open" : "hass:door-closed";
+ return open ? mdiDoorOpen : mdiDoorClosed;
case "damper":
- return open ? "hass:circle" : "hass:circle-slice-8";
+ return open ? mdiCircle : mdiCircleSlice8;
case "shutter":
switch (state) {
case "opening":
- return "hass:arrow-up-box";
+ return mdiArrowUpBox;
case "closing":
- return "hass:arrow-down-box";
+ return mdiArrowDownBox;
case "closed":
- return "hass:window-shutter";
+ return mdiWindowShutter;
default:
- return "hass:window-shutter-open";
+ return mdiWindowShutterOpen;
+ }
+ case "curtain":
+ switch (state) {
+ case "opening":
+ return mdiArrowSplitVertical;
+ case "closing":
+ return mdiArrowCollapseHorizontal;
+ case "closed":
+ return mdiCurtainsClosed;
+ default:
+ return mdiCurtains;
}
case "blind":
- case "curtain":
case "shade":
switch (state) {
case "opening":
- return "hass:arrow-up-box";
+ return mdiArrowUpBox;
case "closing":
- return "hass:arrow-down-box";
+ return mdiArrowDownBox;
case "closed":
- return "hass:blinds";
+ return mdiBlinds;
default:
- return "hass:blinds-open";
+ return mdiBlindsOpen;
}
case "window":
switch (state) {
case "opening":
- return "hass:arrow-up-box";
+ return mdiArrowUpBox;
case "closing":
- return "hass:arrow-down-box";
+ return mdiArrowDownBox;
case "closed":
- return "hass:window-closed";
+ return mdiWindowClosed;
default:
- return "hass:window-open";
+ return mdiWindowOpen;
}
}
switch (state) {
case "opening":
- return "hass:arrow-up-box";
+ return mdiArrowUpBox;
case "closing":
- return "hass:arrow-down-box";
+ return mdiArrowDownBox;
case "closed":
- return "hass:window-closed";
+ return mdiWindowClosed;
default:
- return "hass:window-open";
+ return mdiWindowOpen;
}
};
@@ -84,9 +120,9 @@ export const computeOpenIcon = (stateObj: HassEntity): string => {
case "awning":
case "door":
case "gate":
- return "hass:arrow-expand-horizontal";
+ return mdiArrowExpandHorizontal;
default:
- return "hass:arrow-up";
+ return mdiArrowUp;
}
};
@@ -95,8 +131,8 @@ export const computeCloseIcon = (stateObj: HassEntity): string => {
case "awning":
case "door":
case "gate":
- return "hass:arrow-collapse-horizontal";
+ return mdiArrowCollapseHorizontal;
default:
- return "hass:arrow-down";
+ return mdiArrowDown;
}
};
diff --git a/src/common/entity/domain_icon.ts b/src/common/entity/domain_icon.ts
index 60f27c93aa..3816c925de 100644
--- a/src/common/entity/domain_icon.ts
+++ b/src/common/entity/domain_icon.ts
@@ -1,3 +1,20 @@
+import {
+ mdiAirHumidifierOff,
+ mdiAirHumidifier,
+ mdiLockOpen,
+ mdiLockAlert,
+ mdiLockClock,
+ mdiLock,
+ mdiCastConnected,
+ mdiCast,
+ mdiEmoticonDead,
+ mdiSleep,
+ mdiTimerSand,
+ mdiZWave,
+ mdiClock,
+ mdiCalendar,
+ mdiWeatherNight,
+} from "@mdi/js";
import { HassEntity } from "home-assistant-js-websocket";
/**
* Return the icon to be used for a domain.
@@ -28,36 +45,34 @@ export const domainIcon = (
return coverIcon(compareState, stateObj);
case "humidifier":
- return state && state === "off"
- ? "hass:air-humidifier-off"
- : "hass:air-humidifier";
+ return state && state === "off" ? mdiAirHumidifierOff : mdiAirHumidifier;
case "lock":
switch (compareState) {
case "unlocked":
- return "hass:lock-open";
+ return mdiLockOpen;
case "jammed":
- return "hass:lock-alert";
+ return mdiLockAlert;
case "locking":
case "unlocking":
- return "hass:lock-clock";
+ return mdiLockClock;
default:
- return "hass:lock";
+ return mdiLock;
}
case "media_player":
- return compareState === "playing" ? "hass:cast-connected" : "hass:cast";
+ return compareState === "playing" ? mdiCastConnected : mdiCast;
case "zwave":
switch (compareState) {
case "dead":
- return "hass:emoticon-dead";
+ return mdiEmoticonDead;
case "sleeping":
- return "hass:sleep";
+ return mdiSleep;
case "initializing":
- return "hass:timer-sand";
+ return mdiTimerSand;
default:
- return "hass:z-wave";
+ return mdiZWave;
}
case "sensor": {
@@ -71,17 +86,17 @@ export const domainIcon = (
case "input_datetime":
if (!stateObj?.attributes.has_date) {
- return "hass:clock";
+ return mdiClock;
}
if (!stateObj.attributes.has_time) {
- return "hass:calendar";
+ return mdiCalendar;
}
break;
case "sun":
return stateObj?.state === "above_horizon"
? FIXED_DOMAIN_ICONS[domain]
- : "hass:weather-night";
+ : mdiWeatherNight;
}
if (domain in FIXED_DOMAIN_ICONS) {
diff --git a/src/common/entity/sensor_icon.ts b/src/common/entity/sensor_icon.ts
index fa9bed83ce..9af29ceb3e 100644
--- a/src/common/entity/sensor_icon.ts
+++ b/src/common/entity/sensor_icon.ts
@@ -1,8 +1,9 @@
/** Return an icon representing a sensor state. */
+import { mdiBattery, mdiThermometer } from "@mdi/js";
import { HassEntity } from "home-assistant-js-websocket";
-import { FIXED_DEVICE_CLASS_ICONS, UNIT_C, UNIT_F } from "../const";
-import { batteryIcon } from "./battery_icon";
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;
@@ -12,12 +13,12 @@ export const sensorIcon = (stateObj?: HassEntity): string | undefined => {
}
if (dclass === SENSOR_DEVICE_CLASS_BATTERY) {
- return stateObj ? batteryIcon(stateObj) : "hass:battery";
+ return stateObj ? batteryStateIcon(stateObj) : mdiBattery;
}
const unit = stateObj?.attributes.unit_of_measurement;
if (unit === UNIT_C || unit === UNIT_F) {
- return "hass:thermometer";
+ return mdiThermometer;
}
return undefined;
diff --git a/src/common/entity/state_icon.ts b/src/common/entity/state_icon_path.ts
similarity index 74%
rename from src/common/entity/state_icon.ts
rename to src/common/entity/state_icon_path.ts
index 86bfe50812..24d842caf6 100644
--- a/src/common/entity/state_icon.ts
+++ b/src/common/entity/state_icon_path.ts
@@ -4,13 +4,9 @@ import { DEFAULT_DOMAIN_ICON } from "../const";
import { computeDomain } from "./compute_domain";
import { domainIcon } from "./domain_icon";
-export const stateIcon = (state?: HassEntity) => {
+export const stateIconPath = (state?: HassEntity) => {
if (!state) {
return DEFAULT_DOMAIN_ICON;
}
- if (state.attributes.icon) {
- return state.attributes.icon;
- }
-
return domainIcon(computeDomain(state.entity_id), state);
};
diff --git a/src/common/style/icon_color_css.ts b/src/common/style/icon_color_css.ts
index b636ffa89c..a0905c0ea9 100644
--- a/src/common/style/icon_color_css.ts
+++ b/src/common/style/icon_color_css.ts
@@ -1,57 +1,57 @@
import { css } from "lit";
export const iconColorCSS = css`
- ha-icon[data-domain="alert"][data-state="on"],
- ha-icon[data-domain="automation"][data-state="on"],
- ha-icon[data-domain="binary_sensor"][data-state="on"],
- ha-icon[data-domain="calendar"][data-state="on"],
- ha-icon[data-domain="camera"][data-state="streaming"],
- ha-icon[data-domain="cover"][data-state="open"],
- ha-icon[data-domain="fan"][data-state="on"],
- ha-icon[data-domain="humidifier"][data-state="on"],
- ha-icon[data-domain="light"][data-state="on"],
- ha-icon[data-domain="input_boolean"][data-state="on"],
- ha-icon[data-domain="lock"][data-state="unlocked"],
- ha-icon[data-domain="media_player"][data-state="on"],
- ha-icon[data-domain="media_player"][data-state="paused"],
- ha-icon[data-domain="media_player"][data-state="playing"],
- ha-icon[data-domain="script"][data-state="on"],
- ha-icon[data-domain="sun"][data-state="above_horizon"],
- ha-icon[data-domain="switch"][data-state="on"],
- ha-icon[data-domain="timer"][data-state="active"],
- ha-icon[data-domain="vacuum"][data-state="cleaning"],
- ha-icon[data-domain="group"][data-state="on"],
- ha-icon[data-domain="group"][data-state="home"],
- ha-icon[data-domain="group"][data-state="open"],
- ha-icon[data-domain="group"][data-state="locked"],
- ha-icon[data-domain="group"][data-state="problem"] {
+ ha-state-icon[data-domain="alert"][data-state="on"],
+ ha-state-icon[data-domain="automation"][data-state="on"],
+ ha-state-icon[data-domain="binary_sensor"][data-state="on"],
+ ha-state-icon[data-domain="calendar"][data-state="on"],
+ ha-state-icon[data-domain="camera"][data-state="streaming"],
+ ha-state-icon[data-domain="cover"][data-state="open"],
+ ha-state-icon[data-domain="fan"][data-state="on"],
+ ha-state-icon[data-domain="humidifier"][data-state="on"],
+ ha-state-icon[data-domain="light"][data-state="on"],
+ ha-state-icon[data-domain="input_boolean"][data-state="on"],
+ ha-state-icon[data-domain="lock"][data-state="unlocked"],
+ ha-state-icon[data-domain="media_player"][data-state="on"],
+ ha-state-icon[data-domain="media_player"][data-state="paused"],
+ ha-state-icon[data-domain="media_player"][data-state="playing"],
+ ha-state-icon[data-domain="script"][data-state="on"],
+ ha-state-icon[data-domain="sun"][data-state="above_horizon"],
+ ha-state-icon[data-domain="switch"][data-state="on"],
+ ha-state-icon[data-domain="timer"][data-state="active"],
+ ha-state-icon[data-domain="vacuum"][data-state="cleaning"],
+ ha-state-icon[data-domain="group"][data-state="on"],
+ ha-state-icon[data-domain="group"][data-state="home"],
+ ha-state-icon[data-domain="group"][data-state="open"],
+ ha-state-icon[data-domain="group"][data-state="locked"],
+ ha-state-icon[data-domain="group"][data-state="problem"] {
color: var(--paper-item-icon-active-color, #fdd835);
}
- ha-icon[data-domain="climate"][data-state="cooling"] {
+ ha-state-icon[data-domain="climate"][data-state="cooling"] {
color: var(--cool-color, var(--state-climate-cool-color));
}
- ha-icon[data-domain="climate"][data-state="heating"] {
+ ha-state-icon[data-domain="climate"][data-state="heating"] {
color: var(--heat-color, var(--state-climate-heat-color));
}
- ha-icon[data-domain="climate"][data-state="drying"] {
+ ha-state-icon[data-domain="climate"][data-state="drying"] {
color: var(--dry-color, var(--state-climate-dry-color));
}
- ha-icon[data-domain="alarm_control_panel"] {
+ ha-state-icon[data-domain="alarm_control_panel"] {
color: var(--alarm-color-armed, var(--label-badge-red));
}
- ha-icon[data-domain="alarm_control_panel"][data-state="disarmed"] {
+ ha-state-icon[data-domain="alarm_control_panel"][data-state="disarmed"] {
color: var(--alarm-color-disarmed, var(--label-badge-green));
}
- ha-icon[data-domain="alarm_control_panel"][data-state="pending"],
- ha-icon[data-domain="alarm_control_panel"][data-state="arming"] {
+ ha-state-icon[data-domain="alarm_control_panel"][data-state="pending"],
+ ha-state-icon[data-domain="alarm_control_panel"][data-state="arming"] {
color: var(--alarm-color-pending, var(--label-badge-yellow));
animation: pulse 1s infinite;
}
- ha-icon[data-domain="alarm_control_panel"][data-state="triggered"] {
+ ha-state-icon[data-domain="alarm_control_panel"][data-state="triggered"] {
color: var(--alarm-color-triggered, var(--label-badge-red));
animation: pulse 1s infinite;
}
@@ -68,13 +68,13 @@ export const iconColorCSS = css`
}
}
- ha-icon[data-domain="plant"][data-state="problem"],
- ha-icon[data-domain="zwave"][data-state="dead"] {
+ ha-state-icon[data-domain="plant"][data-state="problem"],
+ ha-state-icon[data-domain="zwave"][data-state="dead"] {
color: var(--state-icon-error-color);
}
/* Color the icon if unavailable */
- ha-icon[data-state="unavailable"] {
+ ha-state-icon[data-state="unavailable"] {
color: var(--state-unavailable-color);
}
`;
diff --git a/src/components/data-table/ha-data-table.ts b/src/components/data-table/ha-data-table.ts
index c3c34b80bb..1362a00f79 100644
--- a/src/components/data-table/ha-data-table.ts
+++ b/src/components/data-table/ha-data-table.ts
@@ -747,10 +747,16 @@ export class HaDataTable extends LitElement {
text-align: right;
}
- .mdc-data-table__cell--icon:first-child ha-icon {
+ .mdc-data-table__cell--icon:first-child ha-icon,
+ .mdc-data-table__cell--icon:first-child ha-state-icon,
+ .mdc-data-table__cell--icon:first-child ha-svg-icon {
margin-left: 8px;
}
- :host([dir="rtl"]) .mdc-data-table__cell--icon:first-child ha-icon {
+ :host([dir="rtl"]) .mdc-data-table__cell--icon:first-child ha-icon,
+ :host([dir="rtl"])
+ .mdc-data-table__cell--icon:first-child
+ ha-state-icon,
+ :host([dir="rtl"]) .mdc-data-table__cell--icon:first-child ha-svg-icon {
margin-left: auto;
margin-right: 8px;
}
diff --git a/src/components/entity/ha-battery-icon.ts b/src/components/entity/ha-battery-icon.ts
index 40233ec4a3..814f9d332e 100644
--- a/src/components/entity/ha-battery-icon.ts
+++ b/src/components/entity/ha-battery-icon.ts
@@ -1,7 +1,7 @@
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
-import { batteryIcon } from "../../common/entity/battery_icon";
-import "../ha-icon";
+import { batteryStateIcon } from "../../common/entity/battery_icon";
+import "../ha-svg-icon";
@customElement("ha-battery-icon")
export class HaBatteryIcon extends LitElement {
@@ -11,9 +11,18 @@ export class HaBatteryIcon extends LitElement {
protected render() {
return html`
-
+
`;
}
}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-battery-icon": HaBatteryIcon;
+ }
+}
diff --git a/src/components/entity/ha-state-label-badge.ts b/src/components/entity/ha-state-label-badge.ts
index 8196d2bdd5..b062078393 100644
--- a/src/components/entity/ha-state-label-badge.ts
+++ b/src/components/entity/ha-state-label-badge.ts
@@ -14,19 +14,18 @@ import secondsToDuration from "../../common/datetime/seconds_to_duration";
import { computeStateDisplay } from "../../common/entity/compute_state_display";
import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { computeStateName } from "../../common/entity/compute_state_name";
-import { stateIcon } from "../../common/entity/state_icon";
import { formatNumber } from "../../common/number/format_number";
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
import { timerTimeRemaining } from "../../data/timer";
import { HomeAssistant } from "../../types";
import "../ha-label-badge";
-import "../ha-icon";
+import "../ha-state-icon";
@customElement("ha-state-label-badge")
export class HaStateLabelBadge extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant;
- @property() public state?: HassEntity;
+ @property({ attribute: false }) public state?: HassEntity;
@property() public name?: string;
@@ -76,15 +75,16 @@ export class HaStateLabelBadge extends LitElement {
// 4. Icon determined via entity state
// 5. Value string as fallback
const domain = computeStateDomain(entityState);
- const icon = this.icon ? this.icon : this._computeIcon(domain, entityState);
- const image = this.icon
+
+ const showIcon = this.icon || this._computeShowIcon(domain, entityState);
+ const image = showIcon
? ""
: this.image
? this.image
: entityState.attributes.entity_picture_local ||
entityState.attributes.entity_picture;
const value =
- !image && !icon ? this._computeValue(domain, entityState) : undefined;
+ !image && !showIcon ? this._computeValue(domain, entityState) : undefined;
return html`
- ${!image && icon ? html`` : ""}
- ${value && !icon && !image
+ ${!image && showIcon
+ ? html``
+ : ""}
+ ${value && !image && !showIcon
? html` 4 ? "big" : ""}
>${value}`
@@ -150,9 +155,9 @@ export class HaStateLabelBadge extends LitElement {
}
}
- private _computeIcon(domain: string, entityState: HassEntity) {
+ private _computeShowIcon(domain: string, entityState: HassEntity): boolean {
if (entityState.state === UNAVAILABLE) {
- return null;
+ return false;
}
switch (domain) {
case "alarm_control_panel":
@@ -162,17 +167,13 @@ export class HaStateLabelBadge extends LitElement {
case "person":
case "scene":
case "sun":
- return stateIcon(entityState);
+ return true;
case "timer":
- return entityState.state === "active"
- ? "hass:timer-outline"
- : "hass:timer-off-outline";
+ return true;
case "sensor":
- return entityState.attributes.device_class === "moon__phase"
- ? stateIcon(entityState)
- : null;
+ return entityState.attributes.device_class === "moon__phase";
default:
- return null;
+ return false;
}
}
diff --git a/src/components/entity/state-badge.ts b/src/components/entity/state-badge.ts
index 2decae9151..404457b33d 100644
--- a/src/components/entity/state-badge.ts
+++ b/src/components/entity/state-badge.ts
@@ -1,3 +1,4 @@
+import { mdiAlert } from "@mdi/js";
import type { HassEntity } from "home-assistant-js-websocket";
import {
css,
@@ -12,10 +13,9 @@ import { ifDefined } from "lit/directives/if-defined";
import { styleMap } from "lit/directives/style-map";
import { computeActiveState } from "../../common/entity/compute_active_state";
import { computeStateDomain } from "../../common/entity/compute_state_domain";
-import { stateIcon } from "../../common/entity/state_icon";
import { iconColorCSS } from "../../common/style/icon_color_css";
import type { HomeAssistant } from "../../types";
-import "../ha-icon";
+import "../ha-state-icon";
export class StateBadge extends LitElement {
public hass?: HomeAssistant;
@@ -39,7 +39,7 @@ export class StateBadge extends LitElement {
// We either need a `stateObj` or one override
if (!stateObj && !this.overrideIcon && !this.overrideImage) {
return html`
-
+
`;
}
@@ -49,18 +49,17 @@ export class StateBadge extends LitElement {
const domain = stateObj ? computeStateDomain(stateObj) : undefined;
- return html`
-
- `;
+ return html``;
}
public willUpdate(changedProps: PropertyValues) {
@@ -154,7 +153,7 @@ export class StateBadge extends LitElement {
:host([icon]:focus) {
background: var(--divider-color);
}
- ha-icon {
+ ha-state-icon {
transition: color 0.3s ease-in-out, filter 0.3s ease-in-out;
}
.missing {
diff --git a/src/components/ha-cover-controls.ts b/src/components/ha-cover-controls.ts
index dd935e0d54..85be8bf786 100644
--- a/src/components/ha-cover-controls.ts
+++ b/src/components/ha-cover-controls.ts
@@ -14,7 +14,6 @@ import { computeCloseIcon, computeOpenIcon } from "../common/entity/cover_icon";
import { UNAVAILABLE } from "../data/entity";
import type { HomeAssistant } from "../types";
import CoverEntity from "../util/cover-model";
-import "./ha-icon";
import "./ha-icon-button";
@customElement("ha-cover-controls")
@@ -49,8 +48,8 @@ class HaCoverControls extends LitElement {
)}
@click=${this._onOpenTap}
.disabled=${this._computeOpenDisabled()}
+ .path=${computeOpenIcon(this.stateObj)}
>
-
-
`;
diff --git a/src/components/ha-icon-input.ts b/src/components/ha-icon-input.ts
deleted file mode 100644
index fb62e3addb..0000000000
--- a/src/components/ha-icon-input.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import "@polymer/paper-input/paper-input";
-import { css, html, LitElement, TemplateResult } from "lit";
-import { customElement, property } from "lit/decorators";
-import { fireEvent } from "../common/dom/fire_event";
-import "./ha-icon";
-
-@customElement("ha-icon-input")
-export class HaIconInput extends LitElement {
- @property() public value?: string;
-
- @property() public label?: string;
-
- @property() public placeholder?: string;
-
- @property({ attribute: "error-message" }) public errorMessage?: string;
-
- @property({ type: Boolean }) public disabled = false;
-
- protected render(): TemplateResult {
- return html`
-
- `;
- }
-
- private _valueChanged(ev: CustomEvent) {
- this.value = ev.detail.value;
- fireEvent(
- this,
- "value-changed",
- { value: ev.detail.value },
- {
- bubbles: false,
- composed: false,
- }
- );
- }
-
- static get styles() {
- return css`
- ha-icon {
- position: absolute;
- bottom: 2px;
- right: 0;
- }
- `;
- }
-}
diff --git a/src/components/ha-icon-picker.ts b/src/components/ha-icon-picker.ts
index added27c6e..96e3a0db6e 100644
--- a/src/components/ha-icon-picker.ts
+++ b/src/components/ha-icon-picker.ts
@@ -46,14 +46,14 @@ export class HaIconPicker extends LitElement {
@property() public placeholder?: string;
+ @property() public fallbackPath?: string;
+
@property({ attribute: "error-message" }) public errorMessage?: string;
@property({ type: Boolean }) public disabled = false;
@query("vaadin-combo-box-light", true) private comboBox!: HTMLElement;
- @property({ type: Boolean }) private _opened = false;
-
protected render(): TemplateResult {
return html`