mirror of
https://github.com/home-assistant/frontend.git
synced 2025-09-02 04:50:25 +00:00
Compare commits
18 Commits
20220322.0
...
add-Use-UU
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1f6243145f | ||
![]() |
27ca61ec85 | ||
![]() |
859f49f3eb | ||
![]() |
40d878689f | ||
![]() |
420e8fe1ff | ||
![]() |
df96199433 | ||
![]() |
f493280f0a | ||
![]() |
cbd030a379 | ||
![]() |
95b80accc9 | ||
![]() |
c522670815 | ||
![]() |
7b6d3c0e36 | ||
![]() |
504b043159 | ||
![]() |
dffc66ccc3 | ||
![]() |
c7e9ee785d | ||
![]() |
079cc39a6e | ||
![]() |
d6a1d5af79 | ||
![]() |
c0dce08e19 | ||
![]() |
f5f8be8276 |
@@ -42,10 +42,11 @@ module.exports = [
|
||||
},
|
||||
{
|
||||
category: "user-test",
|
||||
header: "User Tests",
|
||||
header: "Users",
|
||||
pages: ["user-types", "configuration-menu"],
|
||||
},
|
||||
{
|
||||
category: "design.home-assistant.io",
|
||||
header: "Design Documentation",
|
||||
header: "About",
|
||||
},
|
||||
];
|
||||
|
@@ -45,6 +45,10 @@ class HaGallery extends LitElement {
|
||||
for (const page of group.pages!) {
|
||||
const key = `${group.category}/${page}`;
|
||||
const active = this._page === key;
|
||||
if (!(key in PAGES)) {
|
||||
console.error("Undefined page referenced in sidebar.js:", key);
|
||||
continue;
|
||||
}
|
||||
const title = PAGES[key].metadata.title || page;
|
||||
links.push(html`
|
||||
<a ?active=${active} href=${`#${group.category}/${page}`}>${title}</a>
|
||||
|
17
gallery/src/pages/user-test/user-types.markdown
Normal file
17
gallery/src/pages/user-test/user-types.markdown
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
title: "User types"
|
||||
---
|
||||
|
||||
We have defined three user types for Home Assistant. They are a lean segmentation of users that helps us make decisions throughout the product. User types differ from traditional personas in that the segmentation criteria aren’t demographic and don’t personify a group into a single character with a fictitious background story.
|
||||
|
||||
# Outgrowers
|
||||
|
||||
Users that outgrow big tech smart home solutions. It just needs to work with easy setup via an app.
|
||||
|
||||
# Tinkerers
|
||||
|
||||
Technoid users in home networking and development that know how to code.
|
||||
|
||||
# Questioner
|
||||
|
||||
Users who want more advanced home automation, but need support to make it work.
|
@@ -45,7 +45,6 @@ import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
||||
import "../../../src/layouts/hass-loading-screen";
|
||||
import "../../../src/layouts/hass-subpage";
|
||||
import "../../../src/layouts/hass-tabs-subpage";
|
||||
import { SUPERVISOR_UPDATE_NAMES } from "../../../src/panels/config/dashboard/ha-config-updates";
|
||||
import { HomeAssistant, Route } from "../../../src/types";
|
||||
import { addonArchIsSupported, extractChangelog } from "../util/addon";
|
||||
|
||||
@@ -55,6 +54,12 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
const SUPERVISOR_UPDATE_NAMES = {
|
||||
core: "Home Assistant Core",
|
||||
os: "Home Assistant Operating System",
|
||||
supervisor: "Home Assistant Supervisor",
|
||||
};
|
||||
|
||||
type updateType = "os" | "supervisor" | "core" | "addon";
|
||||
|
||||
const changelogUrl = (
|
||||
|
@@ -72,8 +72,8 @@
|
||||
"@material/mwc-textfield": "0.25.3",
|
||||
"@material/mwc-top-app-bar-fixed": "^0.25.3",
|
||||
"@material/top-app-bar": "14.0.0-canary.261f2db59.0",
|
||||
"@mdi/js": "6.5.95",
|
||||
"@mdi/svg": "6.5.95",
|
||||
"@mdi/js": "6.6.95",
|
||||
"@mdi/svg": "6.6.95",
|
||||
"@polymer/app-layout": "^3.1.0",
|
||||
"@polymer/iron-flex-layout": "^3.0.1",
|
||||
"@polymer/iron-icon": "^3.0.1",
|
||||
|
7
setup.py
7
setup.py
@@ -1,7 +0,0 @@
|
||||
"""
|
||||
Entry point for setuptools. Required for editable installs.
|
||||
TODO: Remove file after updating to pip 21.3
|
||||
"""
|
||||
from setuptools import setup
|
||||
|
||||
setup()
|
@@ -18,6 +18,7 @@ import "./state-badge";
|
||||
|
||||
interface HassEntityWithCachedName extends HassEntity {
|
||||
friendly_name: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
|
||||
@@ -96,6 +97,9 @@ export class HaEntityPicker extends LitElement {
|
||||
|
||||
@property() public entityFilter?: HaEntityPickerEntityFilterFunc;
|
||||
|
||||
@property({ attribute: "item-value-path" }) public itemValuePath =
|
||||
"entity_id";
|
||||
|
||||
@property({ type: Boolean }) public hideClearIcon = false;
|
||||
|
||||
@state() private _opened = false;
|
||||
@@ -144,6 +148,7 @@ export class HaEntityPicker extends LitElement {
|
||||
state: "",
|
||||
last_changed: "",
|
||||
last_updated: "",
|
||||
id: "",
|
||||
context: { id: "", user_id: null, parent_id: null },
|
||||
friendly_name: this.hass!.localize(
|
||||
"ui.components.entity.entity-picker.no_entities"
|
||||
@@ -164,10 +169,15 @@ export class HaEntityPicker extends LitElement {
|
||||
);
|
||||
|
||||
return entityIds
|
||||
.map((key) => ({
|
||||
...hass!.states[key],
|
||||
friendly_name: computeStateName(hass!.states[key]) || key,
|
||||
}))
|
||||
.map((key) => {
|
||||
const stateObj = hass!.states[key];
|
||||
|
||||
return {
|
||||
...stateObj,
|
||||
friendly_name: computeStateName(stateObj) || key,
|
||||
id: stateObj.context.id,
|
||||
};
|
||||
})
|
||||
.sort((entityA, entityB) =>
|
||||
caseInsensitiveStringCompare(
|
||||
entityA.friendly_name,
|
||||
@@ -195,10 +205,15 @@ export class HaEntityPicker extends LitElement {
|
||||
}
|
||||
|
||||
states = entityIds
|
||||
.map((key) => ({
|
||||
...hass!.states[key],
|
||||
friendly_name: computeStateName(hass!.states[key]) || key,
|
||||
}))
|
||||
.map((key) => {
|
||||
const stateObj = hass!.states[key];
|
||||
|
||||
return {
|
||||
...stateObj,
|
||||
friendly_name: computeStateName(stateObj) || key,
|
||||
id: stateObj.context?.id,
|
||||
};
|
||||
})
|
||||
.sort((entityA, entityB) =>
|
||||
caseInsensitiveStringCompare(
|
||||
entityA.friendly_name,
|
||||
@@ -243,6 +258,7 @@ export class HaEntityPicker extends LitElement {
|
||||
state: "",
|
||||
last_changed: "",
|
||||
last_updated: "",
|
||||
id: "",
|
||||
context: { id: "", user_id: null, parent_id: null },
|
||||
friendly_name: this.hass!.localize(
|
||||
"ui.components.entity.entity-picker.no_match"
|
||||
@@ -295,8 +311,8 @@ export class HaEntityPicker extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-combo-box
|
||||
item-value-path="entity_id"
|
||||
item-label-path="friendly_name"
|
||||
.itemValuePath=${this.itemValuePath}
|
||||
.hass=${this.hass}
|
||||
.value=${this._value}
|
||||
.label=${this.label === undefined
|
||||
|
@@ -29,10 +29,11 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) {
|
||||
.hass=${this.hass}
|
||||
.value=${this.value}
|
||||
.label=${this.label}
|
||||
.includeEntities=${this.selector.entity.includeEntities}
|
||||
.excludeEntities=${this.selector.entity.excludeEntities}
|
||||
.includeEntities=${this.selector.entity.include_entities}
|
||||
.excludeEntities=${this.selector.entity.exclude_entities}
|
||||
.entityFilter=${this._filterEntities}
|
||||
.disabled=${this.disabled}
|
||||
.itemValuePath=${!this.selector.entity.use_uuid ? "entity_id" : "id"}
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>`;
|
||||
}
|
||||
@@ -43,8 +44,8 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) {
|
||||
.hass=${this.hass}
|
||||
.value=${this.value}
|
||||
.entityFilter=${this._filterEntities}
|
||||
.includeEntities=${this.selector.entity.includeEntities}
|
||||
.excludeEntities=${this.selector.entity.excludeEntities}
|
||||
.includeEntities=${this.selector.entity.include_entities}
|
||||
.excludeEntities=${this.selector.entity.exclude_entities}
|
||||
></ha-entities-picker>
|
||||
`;
|
||||
}
|
||||
|
@@ -1,35 +1,51 @@
|
||||
export type Selector =
|
||||
| ActionSelector
|
||||
| AddonSelector
|
||||
| AreaSelector
|
||||
| AttributeSelector
|
||||
| EntitySelector
|
||||
| BooleanSelector
|
||||
| ColorRGBSelector
|
||||
| ColorTempSelector
|
||||
| DateSelector
|
||||
| DateTimeSelector
|
||||
| DeviceSelector
|
||||
| DurationSelector
|
||||
| AreaSelector
|
||||
| TargetSelector
|
||||
| EntitySelector
|
||||
| IconSelector
|
||||
| LocationSelector
|
||||
| MediaSelector
|
||||
| NumberSelector
|
||||
| BooleanSelector
|
||||
| TimeSelector
|
||||
| ActionSelector
|
||||
| StringSelector
|
||||
| ObjectSelector
|
||||
| SelectSelector
|
||||
| IconSelector
|
||||
| MediaSelector
|
||||
| StringSelector
|
||||
| TargetSelector
|
||||
| ThemeSelector
|
||||
| LocationSelector
|
||||
| ColorTempSelector
|
||||
| ColorRGBSelector;
|
||||
| TimeSelector;
|
||||
|
||||
export interface EntitySelector {
|
||||
entity: {
|
||||
integration?: string;
|
||||
domain?: string | string[];
|
||||
device_class?: string;
|
||||
multiple?: boolean;
|
||||
includeEntities?: string[];
|
||||
excludeEntities?: string[];
|
||||
export interface ActionSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
action: {};
|
||||
}
|
||||
|
||||
export interface AddonSelector {
|
||||
addon: {
|
||||
name?: string;
|
||||
slug?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AreaSelector {
|
||||
area: {
|
||||
entity?: {
|
||||
integration?: EntitySelector["entity"]["integration"];
|
||||
domain?: EntitySelector["entity"]["domain"];
|
||||
device_class?: EntitySelector["entity"]["device_class"];
|
||||
};
|
||||
device?: {
|
||||
integration?: DeviceSelector["device"]["integration"];
|
||||
manufacturer?: DeviceSelector["device"]["manufacturer"];
|
||||
model?: DeviceSelector["device"]["model"];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,11 +55,23 @@ export interface AttributeSelector {
|
||||
};
|
||||
}
|
||||
|
||||
export interface BooleanSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
boolean: {};
|
||||
}
|
||||
|
||||
export interface ColorRGBSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
color_rgb: {};
|
||||
}
|
||||
|
||||
export interface ColorTempSelector {
|
||||
color_temp: {
|
||||
min_mireds?: number;
|
||||
max_mireds?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface DateSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
date: {};
|
||||
@@ -72,40 +100,50 @@ export interface DurationSelector {
|
||||
duration: {};
|
||||
}
|
||||
|
||||
export interface AddonSelector {
|
||||
addon: {
|
||||
name?: string;
|
||||
slug?: string;
|
||||
export interface EntitySelector {
|
||||
entity: {
|
||||
integration?: string;
|
||||
domain?: string | string[];
|
||||
device_class?: string;
|
||||
multiple?: boolean;
|
||||
use_uuid?: boolean;
|
||||
include_entities?: string[];
|
||||
exclude_entities?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface AreaSelector {
|
||||
area: {
|
||||
entity?: {
|
||||
integration?: EntitySelector["entity"]["integration"];
|
||||
domain?: EntitySelector["entity"]["domain"];
|
||||
device_class?: EntitySelector["entity"]["device_class"];
|
||||
};
|
||||
device?: {
|
||||
integration?: DeviceSelector["device"]["integration"];
|
||||
manufacturer?: DeviceSelector["device"]["manufacturer"];
|
||||
model?: DeviceSelector["device"]["model"];
|
||||
};
|
||||
export interface IconSelector {
|
||||
icon: {
|
||||
placeholder?: string;
|
||||
fallbackPath?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface TargetSelector {
|
||||
target: {
|
||||
entity?: {
|
||||
integration?: EntitySelector["entity"]["integration"];
|
||||
domain?: EntitySelector["entity"]["domain"];
|
||||
device_class?: EntitySelector["entity"]["device_class"];
|
||||
};
|
||||
device?: {
|
||||
integration?: DeviceSelector["device"]["integration"];
|
||||
manufacturer?: DeviceSelector["device"]["manufacturer"];
|
||||
model?: DeviceSelector["device"]["model"];
|
||||
};
|
||||
export interface LocationSelector {
|
||||
location: { radius?: boolean; icon?: string };
|
||||
}
|
||||
|
||||
export interface LocationSelectorValue {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
radius?: number;
|
||||
}
|
||||
|
||||
export interface MediaSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
media: {};
|
||||
}
|
||||
|
||||
export interface MediaSelectorValue {
|
||||
entity_id?: string;
|
||||
media_content_id?: string;
|
||||
media_content_type?: string;
|
||||
metadata?: {
|
||||
title?: string;
|
||||
thumbnail?: string | null;
|
||||
media_class?: string;
|
||||
children_media_class?: string | null;
|
||||
navigateIds?: { media_content_type: string; media_content_id: string }[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -119,28 +157,22 @@ export interface NumberSelector {
|
||||
};
|
||||
}
|
||||
|
||||
export interface ColorTempSelector {
|
||||
color_temp: {
|
||||
min_mireds?: number;
|
||||
max_mireds?: number;
|
||||
export interface ObjectSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
object: {};
|
||||
}
|
||||
|
||||
export interface SelectOption {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface SelectSelector {
|
||||
select: {
|
||||
options: string[] | SelectOption[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface BooleanSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
boolean: {};
|
||||
}
|
||||
|
||||
export interface TimeSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
time: {};
|
||||
}
|
||||
|
||||
export interface ActionSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
action: {};
|
||||
}
|
||||
|
||||
export interface StringSelector {
|
||||
text: {
|
||||
multiline?: boolean;
|
||||
@@ -162,58 +194,25 @@ export interface StringSelector {
|
||||
};
|
||||
}
|
||||
|
||||
export interface ObjectSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
object: {};
|
||||
}
|
||||
|
||||
export interface SelectOption {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface SelectSelector {
|
||||
select: {
|
||||
options: string[] | SelectOption[];
|
||||
export interface TargetSelector {
|
||||
target: {
|
||||
entity?: {
|
||||
integration?: EntitySelector["entity"]["integration"];
|
||||
domain?: EntitySelector["entity"]["domain"];
|
||||
device_class?: EntitySelector["entity"]["device_class"];
|
||||
};
|
||||
device?: {
|
||||
integration?: DeviceSelector["device"]["integration"];
|
||||
manufacturer?: DeviceSelector["device"]["manufacturer"];
|
||||
model?: DeviceSelector["device"]["model"];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface IconSelector {
|
||||
icon: {
|
||||
placeholder?: string;
|
||||
fallbackPath?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ThemeSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
theme: {};
|
||||
}
|
||||
|
||||
export interface MediaSelector {
|
||||
export interface TimeSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
media: {};
|
||||
}
|
||||
|
||||
export interface LocationSelector {
|
||||
location: { radius?: boolean; icon?: string };
|
||||
}
|
||||
|
||||
export interface LocationSelectorValue {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
radius?: number;
|
||||
}
|
||||
|
||||
export interface MediaSelectorValue {
|
||||
entity_id?: string;
|
||||
media_content_id?: string;
|
||||
media_content_type?: string;
|
||||
metadata?: {
|
||||
title?: string;
|
||||
thumbnail?: string | null;
|
||||
media_class?: string;
|
||||
children_media_class?: string | null;
|
||||
navigateIds?: { media_content_type: string; media_content_id: string }[];
|
||||
};
|
||||
time: {};
|
||||
}
|
||||
|
@@ -1,58 +0,0 @@
|
||||
import { HomeAssistant } from "../../types";
|
||||
|
||||
interface SupervisorBaseAvailableUpdates {
|
||||
panel_path?: string;
|
||||
update_type?: string;
|
||||
version_latest?: string;
|
||||
}
|
||||
|
||||
interface SupervisorAddonAvailableUpdates
|
||||
extends SupervisorBaseAvailableUpdates {
|
||||
update_type?: "addon";
|
||||
icon?: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
interface SupervisorCoreAvailableUpdates
|
||||
extends SupervisorBaseAvailableUpdates {
|
||||
update_type?: "core";
|
||||
}
|
||||
|
||||
interface SupervisorOsAvailableUpdates extends SupervisorBaseAvailableUpdates {
|
||||
update_type?: "os";
|
||||
}
|
||||
|
||||
interface SupervisorSupervisorAvailableUpdates
|
||||
extends SupervisorBaseAvailableUpdates {
|
||||
update_type?: "supervisor";
|
||||
}
|
||||
|
||||
export type SupervisorAvailableUpdates =
|
||||
| SupervisorAddonAvailableUpdates
|
||||
| SupervisorCoreAvailableUpdates
|
||||
| SupervisorOsAvailableUpdates
|
||||
| SupervisorSupervisorAvailableUpdates;
|
||||
|
||||
export interface SupervisorAvailableUpdatesResponse {
|
||||
available_updates: SupervisorAvailableUpdates[];
|
||||
}
|
||||
|
||||
export const fetchSupervisorAvailableUpdates = async (
|
||||
hass: HomeAssistant
|
||||
): Promise<SupervisorAvailableUpdates[]> =>
|
||||
(
|
||||
await hass.callWS<SupervisorAvailableUpdatesResponse>({
|
||||
type: "supervisor/api",
|
||||
endpoint: "/available_updates",
|
||||
method: "get",
|
||||
})
|
||||
).available_updates;
|
||||
|
||||
export const refreshSupervisorAvailableUpdates = async (
|
||||
hass: HomeAssistant
|
||||
): Promise<void> =>
|
||||
hass.callWS<void>({
|
||||
type: "supervisor/api",
|
||||
endpoint: "/refresh_updates",
|
||||
method: "post",
|
||||
});
|
@@ -238,12 +238,14 @@ class DataEntryFlowDialog extends LitElement {
|
||||
""
|
||||
: html`
|
||||
<div class="dialog-actions">
|
||||
${this._step
|
||||
${["form", "menu", "external"].includes(
|
||||
this._step?.type as any
|
||||
)
|
||||
? html`
|
||||
<a
|
||||
href=${documentationUrl(
|
||||
this.hass,
|
||||
`/integrations/${this._step.handler}`
|
||||
`/integrations/${this._step!.handler}`
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
|
@@ -189,6 +189,18 @@ export const showConfigFlowDialog = (
|
||||
);
|
||||
},
|
||||
|
||||
renderMenuDescription(hass, step) {
|
||||
const description = hass.localize(
|
||||
`component.${step.handler}.config.step.${step.step_id}.description`,
|
||||
step.description_placeholders
|
||||
);
|
||||
return description
|
||||
? html`
|
||||
<ha-markdown allowsvg breaks .content=${description}></ha-markdown>
|
||||
`
|
||||
: "";
|
||||
},
|
||||
|
||||
renderMenuOption(hass, step, option) {
|
||||
return hass.localize(
|
||||
`component.${step.handler}.config.step.${step.step_id}.menu_options.${option}`,
|
||||
|
@@ -83,6 +83,11 @@ export interface FlowConfig {
|
||||
|
||||
renderMenuHeader(hass: HomeAssistant, step: DataEntryFlowStepMenu): string;
|
||||
|
||||
renderMenuDescription(
|
||||
hass: HomeAssistant,
|
||||
step: DataEntryFlowStepMenu
|
||||
): TemplateResult | "";
|
||||
|
||||
renderMenuOption(
|
||||
hass: HomeAssistant,
|
||||
step: DataEntryFlowStepMenu,
|
||||
|
@@ -142,6 +142,22 @@ export const showOptionsFlowDialog = (
|
||||
);
|
||||
},
|
||||
|
||||
renderMenuDescription(hass, step) {
|
||||
const description = hass.localize(
|
||||
`component.${step.handler}.option.step.${step.step_id}.description`,
|
||||
step.description_placeholders
|
||||
);
|
||||
return description
|
||||
? html`
|
||||
<ha-markdown
|
||||
allowsvg
|
||||
breaks
|
||||
.content=${description}
|
||||
></ha-markdown>
|
||||
`
|
||||
: "";
|
||||
},
|
||||
|
||||
renderMenuOption(hass, step, option) {
|
||||
return hass.localize(
|
||||
`component.${step.handler}.options.step.${step.step_id}.menu_options.${option}`,
|
||||
|
@@ -35,8 +35,14 @@ class StepFlowMenu extends LitElement {
|
||||
translations = this.step.menu_options;
|
||||
}
|
||||
|
||||
const description = this.flowConfig.renderMenuDescription(
|
||||
this.hass,
|
||||
this.step
|
||||
);
|
||||
|
||||
return html`
|
||||
<h2>${this.flowConfig.renderMenuHeader(this.hass, this.step)}</h2>
|
||||
${description ? html`<div class="content">${description}</div>` : ""}
|
||||
<div class="options">
|
||||
${options.map(
|
||||
(option) => html`
|
||||
@@ -69,6 +75,16 @@ class StepFlowMenu extends LitElement {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.content {
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
}
|
||||
.content + .options {
|
||||
margin-top: 8px;
|
||||
}
|
||||
mwc-list-item {
|
||||
--mdc-list-side-padding: 24px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@@ -59,7 +59,9 @@ class HaConfigAutomation extends HassRouterPage {
|
||||
private _getAutomations = memoizeOne(
|
||||
(states: HassEntities): AutomationEntity[] =>
|
||||
Object.values(states).filter(
|
||||
(entity) => computeStateDomain(entity) === "automation"
|
||||
(entity) =>
|
||||
computeStateDomain(entity) === "automation" &&
|
||||
!entity.attributes.restored
|
||||
) as AutomationEntity[]
|
||||
);
|
||||
|
||||
@@ -87,7 +89,7 @@ class HaConfigAutomation extends HassRouterPage {
|
||||
(!changedProps || changedProps.has("route")) &&
|
||||
this._currentPage !== "dashboard"
|
||||
) {
|
||||
const automationId = this.routeTail.path.substr(1);
|
||||
const automationId = decodeURIComponent(this.routeTail.path.substr(1));
|
||||
pageEl.automationId = automationId === "new" ? null : automationId;
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import type { ActionDetail } from "@material/mwc-list";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import {
|
||||
mdiCloudLock,
|
||||
mdiDotsVertical,
|
||||
@@ -5,10 +7,9 @@ import {
|
||||
mdiMagnify,
|
||||
mdiNewBox,
|
||||
} from "@mdi/js";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import type { ActionDetail } from "@material/mwc-list";
|
||||
import "@polymer/app-layout/app-header/app-header";
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import type { HassEntities } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
@@ -18,30 +19,29 @@ import {
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-next";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-menu-button";
|
||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
|
||||
import "../../../components/ha-button-menu";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-icon-next";
|
||||
import "../../../components/ha-menu-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { CloudStatus } from "../../../data/cloud";
|
||||
import {
|
||||
refreshSupervisorAvailableUpdates,
|
||||
SupervisorAvailableUpdates,
|
||||
} from "../../../data/supervisor/root";
|
||||
import { updateCanInstall, UpdateEntity } from "../../../data/update";
|
||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import { showQuickBar } from "../../../dialogs/quick-bar/show-dialog-quick-bar";
|
||||
import "../../../layouts/ha-app-layout";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import "../ha-config-section";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import "./ha-config-navigation";
|
||||
import "./ha-config-updates";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
|
||||
const randomTip = (hass: HomeAssistant) => {
|
||||
const weighted: string[] = [];
|
||||
@@ -113,9 +113,6 @@ class HaConfigDashboard extends LitElement {
|
||||
|
||||
@property() public cloudStatus?: CloudStatus;
|
||||
|
||||
// null means not available
|
||||
@property() public supervisorUpdates?: SupervisorAvailableUpdates[] | null;
|
||||
|
||||
@property() public showAdvanced!: boolean;
|
||||
|
||||
@state() private _tip?: string;
|
||||
@@ -123,6 +120,9 @@ class HaConfigDashboard extends LitElement {
|
||||
private _notifyUpdates = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const canInstallUpdates = this._filterUpdateEntitiesWithInstall(
|
||||
this.hass.states
|
||||
);
|
||||
return html`
|
||||
<ha-app-layout>
|
||||
<app-header fixed slot="header">
|
||||
@@ -160,50 +160,47 @@ class HaConfigDashboard extends LitElement {
|
||||
.isWide=${this.isWide}
|
||||
full-width
|
||||
>
|
||||
${this.supervisorUpdates === undefined
|
||||
? // Hide everything until updates loaded
|
||||
html``
|
||||
: html`${this.supervisorUpdates?.length
|
||||
? html`<ha-card>
|
||||
<ha-config-updates
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.supervisorUpdates=${this.supervisorUpdates}
|
||||
></ha-config-updates>
|
||||
</ha-card>`
|
||||
: ""}
|
||||
<ha-card>
|
||||
${this.narrow && this.supervisorUpdates?.length
|
||||
? html`<div class="title">
|
||||
${this.hass.localize("panel.config")}
|
||||
</div>`
|
||||
: ""}
|
||||
${this.cloudStatus && isComponentLoaded(this.hass, "cloud")
|
||||
? html`
|
||||
<ha-config-navigation
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.showAdvanced=${this.showAdvanced}
|
||||
.pages=${[
|
||||
{
|
||||
component: "cloud",
|
||||
path: "/config/cloud",
|
||||
name: "Home Assistant Cloud",
|
||||
info: this.cloudStatus,
|
||||
iconPath: mdiCloudLock,
|
||||
iconColor: "#3B808E",
|
||||
},
|
||||
]}
|
||||
></ha-config-navigation>
|
||||
`
|
||||
: ""}
|
||||
${canInstallUpdates.length
|
||||
? html`<ha-card>
|
||||
<ha-config-updates
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.updateEntities=${canInstallUpdates}
|
||||
></ha-config-updates>
|
||||
</ha-card>`
|
||||
: ""}
|
||||
<ha-card>
|
||||
${this.narrow && canInstallUpdates.length
|
||||
? html`<div class="title">
|
||||
${this.hass.localize("panel.config")}
|
||||
</div>`
|
||||
: ""}
|
||||
${this.cloudStatus && isComponentLoaded(this.hass, "cloud")
|
||||
? html`
|
||||
<ha-config-navigation
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.showAdvanced=${this.showAdvanced}
|
||||
.pages=${configSections.dashboard}
|
||||
.pages=${[
|
||||
{
|
||||
component: "cloud",
|
||||
path: "/config/cloud",
|
||||
name: "Home Assistant Cloud",
|
||||
info: this.cloudStatus,
|
||||
iconPath: mdiCloudLock,
|
||||
iconColor: "#3B808E",
|
||||
},
|
||||
]}
|
||||
></ha-config-navigation>
|
||||
</ha-card>`}
|
||||
`
|
||||
: ""}
|
||||
<ha-config-navigation
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.showAdvanced=${this.showAdvanced}
|
||||
.pages=${configSections.dashboard}
|
||||
></ha-config-navigation>
|
||||
</ha-card>
|
||||
<div class="tips">
|
||||
<ha-svg-icon .path=${mdiLightbulbOutline}></ha-svg-icon>
|
||||
<span class="tip-word">Tip!</span>
|
||||
@@ -221,11 +218,11 @@ class HaConfigDashboard extends LitElement {
|
||||
this._tip = randomTip(this.hass);
|
||||
}
|
||||
|
||||
if (!changedProps.has("supervisorUpdates") || !this._notifyUpdates) {
|
||||
if (!changedProps.has("hass") || !this._notifyUpdates) {
|
||||
return;
|
||||
}
|
||||
this._notifyUpdates = false;
|
||||
if (this.supervisorUpdates?.length) {
|
||||
if (this._filterUpdateEntitiesWithInstall(this.hass.states).length) {
|
||||
showToast(this, {
|
||||
message: this.hass.localize(
|
||||
"ui.panel.config.updates.updates_refreshed"
|
||||
@@ -238,6 +235,44 @@ class HaConfigDashboard extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _filterUpdateEntities = memoizeOne((entities: HassEntities) =>
|
||||
(
|
||||
Object.values(entities).filter(
|
||||
(entity) => computeStateDomain(entity) === "update"
|
||||
) as UpdateEntity[]
|
||||
).sort((a, b) => {
|
||||
if (a.attributes.title === "Home Assistant Core") {
|
||||
return -3;
|
||||
}
|
||||
if (b.attributes.title === "Home Assistant Core") {
|
||||
return 3;
|
||||
}
|
||||
if (a.attributes.title === "Home Assistant Operating System") {
|
||||
return -2;
|
||||
}
|
||||
if (b.attributes.title === "Home Assistant Operating System") {
|
||||
return 2;
|
||||
}
|
||||
if (a.attributes.title === "Home Assistant Supervisor") {
|
||||
return -1;
|
||||
}
|
||||
if (b.attributes.title === "Home Assistant Supervisor") {
|
||||
return 1;
|
||||
}
|
||||
return caseInsensitiveStringCompare(
|
||||
a.attributes.title || a.attributes.friendly_name || "",
|
||||
b.attributes.title || b.attributes.friendly_name || ""
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
private _filterUpdateEntitiesWithInstall = memoizeOne(
|
||||
(entities: HassEntities) =>
|
||||
this._filterUpdateEntities(entities).filter((entity) =>
|
||||
updateCanInstall(entity)
|
||||
)
|
||||
);
|
||||
|
||||
private _showQuickBar(): void {
|
||||
showQuickBar(this, {
|
||||
commandMode: true,
|
||||
@@ -246,20 +281,24 @@ class HaConfigDashboard extends LitElement {
|
||||
}
|
||||
|
||||
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||
const _entities = this._filterUpdateEntities(this.hass.states).map(
|
||||
(entity) => entity.entity_id
|
||||
);
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
if (isComponentLoaded(this.hass, "hassio")) {
|
||||
if (_entities.length) {
|
||||
this._notifyUpdates = true;
|
||||
await refreshSupervisorAvailableUpdates(this.hass);
|
||||
fireEvent(this, "ha-refresh-supervisor");
|
||||
await this.hass.callService("homeassistant", "update_entity", {
|
||||
entity_id: _entities,
|
||||
});
|
||||
return;
|
||||
}
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.updates.check_unavailable.title"
|
||||
"ui.panel.config.updates.no_update_entities.title"
|
||||
),
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.updates.check_unavailable.description"
|
||||
"ui.panel.config.updates.no_update_entities.description"
|
||||
),
|
||||
warning: true,
|
||||
});
|
||||
|
@@ -1,21 +1,14 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { mdiPackageVariant } from "@mdi/js";
|
||||
import "@polymer/paper-item/paper-icon-item";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/entity/state-badge";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-logo-svg";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { SupervisorAvailableUpdates } from "../../../data/supervisor/root";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "../../../components/ha-icon-next";
|
||||
|
||||
export const SUPERVISOR_UPDATE_NAMES = {
|
||||
core: "Home Assistant Core",
|
||||
os: "Home Assistant Operating System",
|
||||
supervisor: "Home Assistant Supervisor",
|
||||
};
|
||||
import type { UpdateEntity } from "../../../data/update";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
|
||||
@customElement("ha-config-updates")
|
||||
class HaConfigUpdates extends LitElement {
|
||||
@@ -24,62 +17,60 @@ class HaConfigUpdates extends LitElement {
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@property({ attribute: false })
|
||||
public supervisorUpdates?: SupervisorAvailableUpdates[] | null;
|
||||
public updateEntities?: UpdateEntity[];
|
||||
|
||||
@state() private _showAll = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.supervisorUpdates?.length) {
|
||||
if (!this.updateEntities?.length) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const updates =
|
||||
this._showAll || this.supervisorUpdates.length <= 3
|
||||
? this.supervisorUpdates
|
||||
: this.supervisorUpdates.slice(0, 2);
|
||||
this._showAll || this.updateEntities.length <= 3
|
||||
? this.updateEntities
|
||||
: this.updateEntities.slice(0, 2);
|
||||
|
||||
return html`
|
||||
<div class="title">
|
||||
${this.hass.localize("ui.panel.config.updates.title", {
|
||||
count: this.supervisorUpdates.length,
|
||||
count: this.updateEntities.length,
|
||||
})}
|
||||
</div>
|
||||
${updates.map(
|
||||
(update) => html`
|
||||
<a href="/hassio${update.panel_path}">
|
||||
<paper-icon-item>
|
||||
<span slot="item-icon" class="icon">
|
||||
${update.update_type === "addon"
|
||||
? update.icon
|
||||
? html`<img src="/api/hassio${update.icon}" />`
|
||||
: html`<ha-svg-icon
|
||||
.path=${mdiPackageVariant}
|
||||
></ha-svg-icon>`
|
||||
: html`<ha-logo-svg></ha-logo-svg>`}
|
||||
</span>
|
||||
<paper-item-body two-line>
|
||||
${update.update_type === "addon"
|
||||
? update.name
|
||||
: SUPERVISOR_UPDATE_NAMES[update.update_type!]}
|
||||
<div secondary>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.updates.version_available",
|
||||
{
|
||||
version_available: update.version_latest,
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</paper-item-body>
|
||||
${!this.narrow ? html`<ha-icon-next></ha-icon-next>` : ""}
|
||||
</paper-icon-item>
|
||||
</a>
|
||||
(entity) => html`
|
||||
<paper-icon-item
|
||||
@click=${this._openMoreInfo}
|
||||
.entity_id=${entity.entity_id}
|
||||
>
|
||||
<span slot="item-icon" class="icon">
|
||||
<state-badge
|
||||
.title=${entity.attributes.title ||
|
||||
entity.attributes.friendly_name}
|
||||
.stateObj=${entity}
|
||||
slot="item-icon"
|
||||
></state-badge>
|
||||
</span>
|
||||
<paper-item-body two-line>
|
||||
${entity.attributes.title || entity.attributes.friendly_name}
|
||||
<div secondary>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.updates.version_available",
|
||||
{
|
||||
version_available: entity.attributes.latest_version,
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</paper-item-body>
|
||||
${!this.narrow ? html`<ha-icon-next></ha-icon-next>` : ""}
|
||||
</paper-icon-item>
|
||||
`
|
||||
)}
|
||||
${!this._showAll && this.supervisorUpdates.length >= 4
|
||||
${!this._showAll && this.updateEntities.length >= 4
|
||||
? html`
|
||||
<button class="show-more" @click=${this._showAllClicked}>
|
||||
${this.hass.localize("ui.panel.config.updates.more_updates", {
|
||||
count: this.supervisorUpdates!.length - updates.length,
|
||||
count: this.updateEntities!.length - updates.length,
|
||||
})}
|
||||
</button>
|
||||
`
|
||||
@@ -87,6 +78,12 @@ class HaConfigUpdates extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _openMoreInfo(ev: MouseEvent): void {
|
||||
fireEvent(this, "hass-more-info", {
|
||||
entityId: (ev.currentTarget as any).entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _showAllClicked() {
|
||||
this._showAll = true;
|
||||
}
|
||||
@@ -99,25 +96,11 @@ class HaConfigUpdates extends LitElement {
|
||||
padding: 16px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.icon {
|
||||
display: inline-flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
img,
|
||||
ha-svg-icon,
|
||||
ha-logo-svg {
|
||||
--mdc-icon-size: 32px;
|
||||
max-height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
ha-logo-svg {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
ha-icon-next {
|
||||
color: var(--secondary-text-color);
|
||||
height: 24px;
|
||||
@@ -139,6 +122,9 @@ class HaConfigUpdates extends LitElement {
|
||||
outline: none;
|
||||
text-decoration: underline;
|
||||
}
|
||||
paper-icon-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@@ -27,10 +27,6 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||
import { listenMediaQuery } from "../../common/dom/media_query";
|
||||
import { CloudStatus, fetchCloudStatus } from "../../data/cloud";
|
||||
import {
|
||||
fetchSupervisorAvailableUpdates,
|
||||
SupervisorAvailableUpdates,
|
||||
} from "../../data/supervisor/root";
|
||||
import "../../layouts/hass-loading-screen";
|
||||
import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
|
||||
import { PageNavigation } from "../../layouts/hass-tabs-subpage";
|
||||
@@ -397,8 +393,6 @@ class HaPanelConfig extends HassRouterPage {
|
||||
|
||||
@state() private _cloudStatus?: CloudStatus;
|
||||
|
||||
@state() private _supervisorUpdates?: SupervisorAvailableUpdates[] | null;
|
||||
|
||||
private _listeners: Array<() => void> = [];
|
||||
|
||||
public connectedCallback() {
|
||||
@@ -433,19 +427,7 @@ class HaPanelConfig extends HassRouterPage {
|
||||
}
|
||||
});
|
||||
}
|
||||
if (isComponentLoaded(this.hass, "hassio")) {
|
||||
this._loadSupervisorUpdates();
|
||||
this.addEventListener("ha-refresh-supervisor", () => {
|
||||
this._loadSupervisorUpdates();
|
||||
});
|
||||
this.addEventListener("connection-status", (ev) => {
|
||||
if (ev.detail === "connected") {
|
||||
this._loadSupervisorUpdates();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this._supervisorUpdates = null;
|
||||
}
|
||||
|
||||
this.addEventListener("ha-refresh-cloud-status", () =>
|
||||
this._updateCloudStatus()
|
||||
);
|
||||
@@ -476,7 +458,6 @@ class HaPanelConfig extends HassRouterPage {
|
||||
isWide,
|
||||
narrow: this.narrow,
|
||||
cloudStatus: this._cloudStatus,
|
||||
supervisorUpdates: this._supervisorUpdates,
|
||||
});
|
||||
} else {
|
||||
el.route = this.routeTail;
|
||||
@@ -485,7 +466,6 @@ class HaPanelConfig extends HassRouterPage {
|
||||
el.isWide = isWide;
|
||||
el.narrow = this.narrow;
|
||||
el.cloudStatus = this._cloudStatus;
|
||||
el.supervisorUpdates = this._supervisorUpdates;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,16 +483,6 @@ class HaPanelConfig extends HassRouterPage {
|
||||
setTimeout(() => this._updateCloudStatus(), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
private async _loadSupervisorUpdates(): Promise<void> {
|
||||
try {
|
||||
this._supervisorUpdates = await fetchSupervisorAvailableUpdates(
|
||||
this.hass
|
||||
);
|
||||
} catch (err) {
|
||||
this._supervisorUpdates = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -173,6 +173,9 @@ export class HaPickThemeRow extends LitElement {
|
||||
}
|
||||
|
||||
private _supportsModeSelection(themeName: string): boolean {
|
||||
if (!(themeName in this.hass.themes.themes)) {
|
||||
return false; // User's theme no longer exists
|
||||
}
|
||||
return "modes" in this.hass.themes.themes[themeName];
|
||||
}
|
||||
|
||||
|
@@ -1079,9 +1079,9 @@
|
||||
"learn_more": "Learn more"
|
||||
},
|
||||
"updates": {
|
||||
"check_unavailable": {
|
||||
"no_update_entities": {
|
||||
"title": "Unable to check for updates",
|
||||
"description": "You need to run the Home Assistant operating system to be able to check and install updates from the Home Assistant user interface."
|
||||
"description": "You do not have any integrations that provide updates."
|
||||
},
|
||||
"check_updates": "Check for updates",
|
||||
"no_new_updates": "No new updates found",
|
||||
|
20
yarn.lock
20
yarn.lock
@@ -2975,17 +2975,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mdi/js@npm:6.5.95":
|
||||
version: 6.5.95
|
||||
resolution: "@mdi/js@npm:6.5.95"
|
||||
checksum: b1db7713d216c119f584bf973514a2f9d8f2e671e91bf19ce8e56cfa7a9843c0a060328e794507ac31f2bded1032123294f39ff8e987ea5acb2719ab522ef146
|
||||
"@mdi/js@npm:6.6.95":
|
||||
version: 6.6.95
|
||||
resolution: "@mdi/js@npm:6.6.95"
|
||||
checksum: 4cf8c48156f0e9ff67e4394cd428158bd164b1a6b7ca1aa70fc6a6aee91cfede9eba56720eb7d13fa57315ac636e9519a62dedd3cd2a9708aa11f2e3624ddbff
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mdi/svg@npm:6.5.95":
|
||||
version: 6.5.95
|
||||
resolution: "@mdi/svg@npm:6.5.95"
|
||||
checksum: 2d45221d042d52d54c85eaf672a5f3697ed5201607fa38a6e235ee2e60d1c3c25d456a284f19ce47b5f06418cacfee29e8fecf6580b8c28538fd26044becaf1a
|
||||
"@mdi/svg@npm:6.6.95":
|
||||
version: 6.6.95
|
||||
resolution: "@mdi/svg@npm:6.6.95"
|
||||
checksum: 59b79db945847a3d981351418e0e7a457b831e09846fa751d44e80df8fb4cd19ef12bc889538ed2945d2638e522aa7ea5b1f97997e19dd68345f5d7bf5cad5e6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -9048,8 +9048,8 @@ fsevents@^1.2.7:
|
||||
"@material/mwc-textfield": 0.25.3
|
||||
"@material/mwc-top-app-bar-fixed": ^0.25.3
|
||||
"@material/top-app-bar": 14.0.0-canary.261f2db59.0
|
||||
"@mdi/js": 6.5.95
|
||||
"@mdi/svg": 6.5.95
|
||||
"@mdi/js": 6.6.95
|
||||
"@mdi/svg": 6.6.95
|
||||
"@open-wc/dev-server-hmr": ^0.0.2
|
||||
"@polymer/app-layout": ^3.1.0
|
||||
"@polymer/iron-flex-layout": ^3.0.1
|
||||
|
Reference in New Issue
Block a user