mirror of
https://github.com/home-assistant/frontend.git
synced 2026-06-06 08:22:08 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f89c47d218 |
@@ -8,7 +8,6 @@ You are an assistant helping with development of the Home Assistant frontend. Th
|
||||
|
||||
- [Quick Reference](#quick-reference)
|
||||
- [Core Architecture](#core-architecture)
|
||||
- [State Access: Contexts Instead of `hass`](#state-access-contexts-instead-of-hass)
|
||||
- [Development Standards](#development-standards)
|
||||
- [Component Library](#component-library)
|
||||
- [Common Patterns](#common-patterns)
|
||||
@@ -53,57 +52,6 @@ The Home Assistant frontend is a modern web application that:
|
||||
- Communicates with the backend via WebSocket API
|
||||
- Provides comprehensive theming and internationalization
|
||||
|
||||
## State Access: Contexts Instead of `hass`
|
||||
|
||||
Every component used to take the whole `hass: HomeAssistant` object — a god-object that re-renders on any unrelated `hass` change, forces tests to mock everything, and hides what a component actually reads. We're moving leaf components to **fine-grained [Lit context](https://lit.dev/docs/data/context/)**: consume only the slice you need and re-render only when it changes.
|
||||
|
||||
For new code, consume the matching context instead of adding a `hass` property. `hass` stays for container components that own it and feed the providers; the canonical migration is [`hui-button-card.ts`](src/panels/lovelace/cards/hui-button-card.ts). Infrastructure: contexts in [`src/data/context/index.ts`](src/data/context/index.ts), the `consume…` helpers in [`src/common/decorators/consume-context-entry.ts`](src/common/decorators/consume-context-entry.ts), and `@transform` in [`src/common/decorators/transform.ts`](src/common/decorators/transform.ts). Providers are wired automatically by `contextMixin` on `HassBaseEl` — you only consume.
|
||||
|
||||
### Contexts
|
||||
|
||||
Consume the narrowest context that covers your reads:
|
||||
|
||||
| Context | Replaces |
|
||||
| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
||||
| `statesContext` | `hass.states` |
|
||||
| `entitiesContext` / `devicesContext` / `areasContext` / `floorsContext` | `hass.entities` / `.devices` / `.areas` / `.floors` (or `registriesContext` for all four) |
|
||||
| `servicesContext` | `hass.services` |
|
||||
| `internationalizationContext` | `hass.localize`, `hass.locale`, `hass.language` |
|
||||
| `formattersContext` | `hass.formatEntityName`, `hass.formatEntityState`, `hass.formatEntityAttributeName`, … |
|
||||
| `configContext` | `hass.config`, `hass.user`, `hass.auth`, `hass.userData` |
|
||||
| `connectionContext` | `hass.connection`, `hass.connected`, `hass.hassUrl` |
|
||||
| `apiContext` | `hass.callService`, `hass.callApi`, `hass.callWS`, `hass.sendWS`, `hass.fetchWithAuth` |
|
||||
| `uiContext` | `hass.themes`, `hass.selectedTheme`, `hass.panels`, `hass.dockedSidebar`, … |
|
||||
| `narrowViewportContext` | narrow-layout boolean |
|
||||
|
||||
Lazy contexts (subscribe on first consumer, tear down after the last): `labelsContext`, `fullEntitiesContext`, `configEntriesContext`, `manifestsContext`. The single-field contexts (`localizeContext`, `themesContext`, `userContext`, …) are **deprecated** — use the grouped ones above.
|
||||
|
||||
### Consuming
|
||||
|
||||
Use the `consume…` helpers for entity-scoped and `localize` reads. `entityIdPath` is resolved against `this`, so these watch `this._config.entity`:
|
||||
|
||||
```ts
|
||||
@state() @consumeEntityState({ entityIdPath: ["_config", "entity"] })
|
||||
private _stateObj?: HassEntity; // consumeEntityStates(...) for a record of several
|
||||
|
||||
@state() @consumeEntityRegistryEntry({ entityIdPath: ["_config", "entity"] })
|
||||
private _entity?: EntityRegistryDisplayEntry;
|
||||
|
||||
@state() @consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
```
|
||||
|
||||
For any other single field, pair `@consume` with `@transform`:
|
||||
|
||||
```ts
|
||||
@state()
|
||||
@consume({ context: uiContext, subscribe: true })
|
||||
@transform<HomeAssistantUI, Themes>({ transformer: ({ themes }) => themes })
|
||||
private _themes!: Themes;
|
||||
```
|
||||
|
||||
`@transform`'s `watch` option re-runs the transformer when a host prop changes — needed when an entity id is computed, since `consumeEntityState` only watches the first path segment. To consume a whole group untransformed, drop `@transform` and type it `ContextType<typeof statesContext>`.
|
||||
|
||||
## Development Standards
|
||||
|
||||
### Code Quality Requirements
|
||||
@@ -188,7 +136,6 @@ export class HaMyComponent extends LitElement {
|
||||
### Data Management
|
||||
|
||||
- **Use WebSocket API**: All backend communication via home-assistant-js-websocket
|
||||
- **Prefer contexts over `hass`**: For state reads, consume the relevant Lit context instead of taking the whole `hass` object — see [State Access: Contexts Instead of `hass`](#state-access-contexts-instead-of-hass)
|
||||
- **Cache appropriately**: Use collections and caching for frequently accessed data
|
||||
- **Handle errors gracefully**: All API calls should have error handling
|
||||
- **Update real-time**: Subscribe to state changes for live updates
|
||||
@@ -329,7 +276,7 @@ fireEvent(this, "show-dialog", {
|
||||
|
||||
- **`variant`** (color): `"brand"` (default), `"neutral"`, `"danger"`, `"warning"`, `"success"`
|
||||
- **`appearance`** (fill style): `"accent"`, `"filled"`, `"outlined"`, `"plain"`
|
||||
- **`size`**: `"xs"` (extra small, 40px), `"s"` (small, 32px), `"m"` (medium, 40px - default), `"l"` (large, 48px), `"xl"` (extra large, 40px)
|
||||
- **`size`**: `"small"` (32px), `"medium"` (40px - default), `"large"` (48px)
|
||||
|
||||
Common patterns:
|
||||
|
||||
|
||||
@@ -77,22 +77,9 @@ jobs:
|
||||
env:
|
||||
GITHUB_REF: ${{ github.ref }}
|
||||
run: |
|
||||
# Sleep to give pypi time to populate the new version across mirrors
|
||||
sleep 240
|
||||
version=$(echo "$GITHUB_REF" | awk -F"/" '{print $NF}' )
|
||||
# Wait for the package to become available on PyPI
|
||||
echo "Waiting for home-assistant-frontend==$version to appear on PyPI..."
|
||||
for i in $(seq 1 30); do
|
||||
status=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/home-assistant-frontend/$version/json")
|
||||
if [ "$status" = "200" ]; then
|
||||
echo "Package is available on PyPI!"
|
||||
break
|
||||
fi
|
||||
if [ "$i" = "30" ]; then
|
||||
echo "Timed out waiting for package to appear on PyPI"
|
||||
exit 1
|
||||
fi
|
||||
echo "Not available yet (HTTP $status), retrying in 30 seconds... ($i/30)"
|
||||
sleep 30
|
||||
done
|
||||
echo "home-assistant-frontend==$version" > ./requirements.txt
|
||||
|
||||
# home-assistant/wheels doesn't support SHA pinning
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
diff --git a/dist/tinykeys.cjs b/dist/tinykeys.cjs
|
||||
index 08c98b6eff3b8fb4b727fe8e6b096951d6ef6347..9c44f14862f582766ea1733b6dc0e97f962800d8 100644
|
||||
--- a/dist/tinykeys.cjs
|
||||
+++ b/dist/tinykeys.cjs
|
||||
@@ -61,6 +61,18 @@ function defaultKeybindingsHandlerIgnore(event) {
|
||||
function getModifierState(event, mod) {
|
||||
return typeof event.getModifierState === "function" ? event.getModifierState(mod) || ALT_GRAPH_ALIASES.includes(mod) && event.getModifierState("AltGraph") : false;
|
||||
}
|
||||
+function splitKeybindingPress(press) {
|
||||
+ let parts = [];
|
||||
+ let start = 0;
|
||||
+ for (let index = 0; index < press.length; index++) {
|
||||
+ if (press[index] === "+" && /[\w\]]/.test(press[index - 1] || "")) {
|
||||
+ parts.push(press.slice(start, index));
|
||||
+ start = index + 1;
|
||||
+ }
|
||||
+ }
|
||||
+ parts.push(press.slice(start));
|
||||
+ return parts;
|
||||
+}
|
||||
/**
|
||||
* Parses a keybinding string into its parts.
|
||||
*
|
||||
@@ -76,10 +88,10 @@ function getModifierState(event, mod) {
|
||||
*/
|
||||
function parseKeybinding(str) {
|
||||
return str.trim().split(" ").map((press) => {
|
||||
- let parts = press.split(/(?<=\w|\])\+/);
|
||||
+ let parts = splitKeybindingPress(press);
|
||||
let last = parts.pop();
|
||||
let regex = last.match(/^\((.+)\)$/);
|
||||
- let key = regex ? new RegExp(`^(?:${regex[1]})$`, "iv") : last;
|
||||
+ let key = regex ? new RegExp(`^(?:${regex[1]})$`, "i") : last;
|
||||
let requiredModifiers = [];
|
||||
let optionalModifiers = [];
|
||||
for (const part of parts) {
|
||||
@@ -201,5 +213,3 @@ exports.defaultKeybindingsHandlerIgnore = defaultKeybindingsHandlerIgnore;
|
||||
exports.matchKeybindingPress = matchKeybindingPress;
|
||||
exports.parseKeybinding = parseKeybinding;
|
||||
exports.tinykeys = tinykeys;
|
||||
-
|
||||
-//# sourceMappingURL=tinykeys.cjs.map
|
||||
\ No newline at end of file
|
||||
diff --git a/dist/tinykeys.mjs b/dist/tinykeys.mjs
|
||||
index c289972d2728e03d9b272268c38fd3392e8845bf..e22897b00aae6cdb0dbbb971445227c07be52918 100644
|
||||
--- a/dist/tinykeys.mjs
|
||||
+++ b/dist/tinykeys.mjs
|
||||
@@ -60,6 +60,18 @@ function defaultKeybindingsHandlerIgnore(event) {
|
||||
function getModifierState(event, mod) {
|
||||
return typeof event.getModifierState === "function" ? event.getModifierState(mod) || ALT_GRAPH_ALIASES.includes(mod) && event.getModifierState("AltGraph") : false;
|
||||
}
|
||||
+function splitKeybindingPress(press) {
|
||||
+ let parts = [];
|
||||
+ let start = 0;
|
||||
+ for (let index = 0; index < press.length; index++) {
|
||||
+ if (press[index] === "+" && /[\w\]]/.test(press[index - 1] || "")) {
|
||||
+ parts.push(press.slice(start, index));
|
||||
+ start = index + 1;
|
||||
+ }
|
||||
+ }
|
||||
+ parts.push(press.slice(start));
|
||||
+ return parts;
|
||||
+}
|
||||
/**
|
||||
* Parses a keybinding string into its parts.
|
||||
*
|
||||
@@ -75,10 +87,10 @@ function getModifierState(event, mod) {
|
||||
*/
|
||||
function parseKeybinding(str) {
|
||||
return str.trim().split(" ").map((press) => {
|
||||
- let parts = press.split(/(?<=\w|\])\+/);
|
||||
+ let parts = splitKeybindingPress(press);
|
||||
let last = parts.pop();
|
||||
let regex = last.match(/^\((.+)\)$/);
|
||||
- let key = regex ? new RegExp(`^(?:${regex[1]})$`, "iv") : last;
|
||||
+ let key = regex ? new RegExp(`^(?:${regex[1]})$`, "i") : last;
|
||||
let requiredModifiers = [];
|
||||
let optionalModifiers = [];
|
||||
for (const part of parts) {
|
||||
@@ -196,5 +208,3 @@ function tinykeys(target, keybindingMap, options = {}) {
|
||||
}
|
||||
//#endregion
|
||||
export { createKeybindingsHandler, defaultKeybindingsHandlerIgnore, matchKeybindingPress, parseKeybinding, tinykeys };
|
||||
-
|
||||
-//# sourceMappingURL=tinykeys.mjs.map
|
||||
\ No newline at end of file
|
||||
+940
File diff suppressed because one or more lines are too long
Vendored
-944
File diff suppressed because one or more lines are too long
+1
-1
@@ -13,4 +13,4 @@ nodeLinker: node-modules
|
||||
|
||||
npmMinimalAgeGate: 3d
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.16.0.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.15.0.cjs
|
||||
|
||||
@@ -29,7 +29,7 @@ const LICENSE_OVERRIDES = [
|
||||
// type-fest ships two license files (MIT for code, CC0 for types).
|
||||
// We use the MIT license since that covers the bundled code.
|
||||
packageName: "type-fest",
|
||||
version: "5.7.0",
|
||||
version: "5.6.0",
|
||||
licensePath: path.resolve(
|
||||
paths.root_dir,
|
||||
"node_modules/type-fest/license-mit"
|
||||
|
||||
+48
-48
@@ -94,55 +94,55 @@ class HaGallery extends LitElement {
|
||||
<div slot="title">
|
||||
${PAGES[this._page].metadata.title || this._page.split("/")[1]}
|
||||
</div>
|
||||
<div class="content">
|
||||
${PAGES[this._page].description
|
||||
? html`
|
||||
<page-description .page=${this._page}></page-description>
|
||||
`
|
||||
: ""}
|
||||
${dynamicElement(`demo-${this._page.replace("/", "-")}`)}
|
||||
</div>
|
||||
<div class="page-footer">
|
||||
<div class="edit-docs">
|
||||
<div class="header">Help us to improve our documentation</div>
|
||||
<div class="secondary">
|
||||
Suggest an edit to this page, or provide/view feedback for
|
||||
this page.
|
||||
</div>
|
||||
<div>
|
||||
${PAGES[this._page].description ||
|
||||
Object.keys(PAGES[this._page].metadata).length > 0
|
||||
? html`
|
||||
<a
|
||||
href=${`${GITHUB_DEMO_URL}${this._page}.markdown`}
|
||||
target="_blank"
|
||||
>
|
||||
Edit text
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
${PAGES[this._page].demo
|
||||
? html`
|
||||
<a
|
||||
href=${`${GITHUB_DEMO_URL}${this._page}.ts`}
|
||||
target="_blank"
|
||||
>
|
||||
Edit demo
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
<div class="rtl-toggle">
|
||||
<ha-icon-button
|
||||
@click=${this._toggleRtl}
|
||||
.label=${this._rtl ? "Switch to LTR" : "Switch to RTL"}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiSwapHorizontal}></ha-svg-icon>
|
||||
</ha-icon-button>
|
||||
</div>
|
||||
</div>
|
||||
</ha-top-app-bar-fixed>
|
||||
<div class="content">
|
||||
${PAGES[this._page].description
|
||||
? html`
|
||||
<page-description .page=${this._page}></page-description>
|
||||
`
|
||||
: ""}
|
||||
${dynamicElement(`demo-${this._page.replace("/", "-")}`)}
|
||||
</div>
|
||||
<div class="page-footer">
|
||||
<div class="edit-docs">
|
||||
<div class="header">Help us to improve our documentation</div>
|
||||
<div class="secondary">
|
||||
Suggest an edit to this page, or provide/view feedback for this
|
||||
page.
|
||||
</div>
|
||||
<div>
|
||||
${PAGES[this._page].description ||
|
||||
Object.keys(PAGES[this._page].metadata).length > 0
|
||||
? html`
|
||||
<a
|
||||
href=${`${GITHUB_DEMO_URL}${this._page}.markdown`}
|
||||
target="_blank"
|
||||
>
|
||||
Edit text
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
${PAGES[this._page].demo
|
||||
? html`
|
||||
<a
|
||||
href=${`${GITHUB_DEMO_URL}${this._page}.ts`}
|
||||
target="_blank"
|
||||
>
|
||||
Edit demo
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
<div class="rtl-toggle">
|
||||
<ha-icon-button
|
||||
@click=${this._toggleRtl}
|
||||
.label=${this._rtl ? "Switch to LTR" : "Switch to RTL"}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiSwapHorizontal}></ha-svg-icon>
|
||||
</ha-icon-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ha-drawer>
|
||||
<notification-manager
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
title: "Brand Personality"
|
||||
---
|
||||
|
||||
# Brand Personality
|
||||
|
||||
These five traits describe who Home Assistant is, not how it speaks. Tone of voice – the playfulness, the informality, the warmth, etc – should flow naturally from these, and will help guide writers on how to bring the brand personality to life.
|
||||
|
||||
The first four traits are relational: they describe how Home Assistant behaves toward its users.
|
||||
_Welcoming_ is about how we receive them.\
|
||||
_Candid_ is about how we communicate with them.\
|
||||
_Supportive_ is about how we help them.\
|
||||
_Generous_ is about how/what we give to them.\
|
||||
If any of these feel similar, it’s because they’re all expressions of the same underlying character, just in different moments of the user relationship.
|
||||
|
||||
_Independent_ is different. It’s foundational: it describes who Home Assistant is at its core.\
|
||||
And it’s because of that independence that the other four traits feel genuine rather than performed. A corporate brand can try to be welcoming, candid, supportive, and generous,
|
||||
but without independence, those traits will always be managed and moderated.
|
||||
|
||||
## Welcoming
|
||||
|
||||
**Warm and open, kind, friendly, approachable, accommodating**\
|
||||
_But not: people pleasing, appeasing, sycophantic, ingratiating_\
|
||||
Home Assistant feels like a knowledgeable friend, not a product. We meet you at your own level, never talk down to you, and make you feel valued regardless of your technical ability. This isn’t performative, it’s expressed naturally in the small things: how errors are explained, documentation is written, and how the community talks to newcomers.
|
||||
|
||||
## Candid
|
||||
|
||||
**Direct, honest, transparent, unpretentious**\
|
||||
_But not: unfriendly, rude, blunt, unempathetic_\
|
||||
Home Assistant says what it means. We don’t hide complexity behind false simplicity, fall back on marketing fluff, or pretend limitations don’t exist. We respect users enough to be straight with them: about what Home Assistant can do, what it can't, and why it works the way it does. This is what builds real trust with users.
|
||||
|
||||
## Supportive
|
||||
|
||||
**Helpful, guiding, encouraging, empathetic, genuine**\
|
||||
_But not: directive, hollow, condescending, over-bearing_\
|
||||
Home Assistant always has your back. Whether you’re just starting out or deep into a complex setup, we steer you forward without taking over. Our support is genuine: practical, patient, and there when it’s needed most. Because Home Assistant wants every user to succeed in building a smart home with privacy, choice, and sustainability at its heart.
|
||||
|
||||
## Generous
|
||||
|
||||
**Empowering, trusting, giving, sharing**\
|
||||
_But not: overwhelming, indiscriminate, patronizing, controlling_\
|
||||
Home Assistant gives you everything you need today, and as your setup evolves. There are no strings attached: no artificial limits, features locked behind tiers, or deceptive patterns designed to tie you to a closed platform. We trust users with control, access, and transparency. This generosity is reciprocal: the time, knowledge, and care our community gives freely is what keeps Home Assistant thriving and truly open.
|
||||
|
||||
## Independent
|
||||
|
||||
**Principled, liberated, confident, imperfect**\
|
||||
_But not: conceited, obstinate, erratic, insular_\
|
||||
Home Assistant doesn’t feel the need to behave like an established tech brand or follow corporate rules. With no shareholders or VCs to answer to, we can say what we think, do things our own way, and not take ourselves too seriously. This freedom of spirit comes from the confidence of knowing our own values, and that our community shares them.
|
||||
+10
-10
@@ -70,12 +70,12 @@
|
||||
"@replit/codemirror-indentation-markers": "6.5.3",
|
||||
"@swc/helpers": "0.5.23",
|
||||
"@thomasloven/round-slider": "0.6.0",
|
||||
"@tsparticles/engine": "4.1.2",
|
||||
"@tsparticles/preset-links": "4.1.2",
|
||||
"@tsparticles/engine": "4.1.1",
|
||||
"@tsparticles/preset-links": "4.1.1",
|
||||
"@vibrant/color": "4.0.4",
|
||||
"@webcomponents/scoped-custom-element-registry": "0.0.10",
|
||||
"@webcomponents/webcomponentsjs": "2.8.0",
|
||||
"barcode-detector": "3.2.0",
|
||||
"barcode-detector": "3.1.3",
|
||||
"cally": "0.9.2",
|
||||
"color-name": "2.1.0",
|
||||
"comlink": "4.4.2",
|
||||
@@ -88,12 +88,12 @@
|
||||
"dialog-polyfill": "0.5.6",
|
||||
"echarts": "6.1.0",
|
||||
"element-internals-polyfill": "3.0.2",
|
||||
"fuse.js": "7.4.1",
|
||||
"fuse.js": "7.4.0",
|
||||
"google-timezones-json": "1.2.0",
|
||||
"gulp-zopfli-green": "7.0.0",
|
||||
"hls.js": "1.6.16",
|
||||
"home-assistant-js-websocket": "9.6.0",
|
||||
"idb-keyval": "6.2.5",
|
||||
"idb-keyval": "6.2.4",
|
||||
"intl-messageformat": "11.2.7",
|
||||
"js-yaml": "4.2.0",
|
||||
"leaflet": "1.9.4",
|
||||
@@ -114,7 +114,7 @@
|
||||
"sortablejs": "patch:sortablejs@npm%3A1.15.6#~/.yarn/patches/sortablejs-npm-1.15.6-3235a8f83b.patch",
|
||||
"stacktrace-js": "2.0.2",
|
||||
"superstruct": "2.0.2",
|
||||
"tinykeys": "patch:tinykeys@npm%3A4.0.0#~/.yarn/patches/tinykeys-npm-4.0.0-a6ca3fd771.patch",
|
||||
"tinykeys": "4.0.0",
|
||||
"weekstart": "2.0.0",
|
||||
"workbox-cacheable-response": "7.4.1",
|
||||
"workbox-core": "7.4.1",
|
||||
@@ -137,7 +137,7 @@
|
||||
"@octokit/plugin-retry": "8.1.0",
|
||||
"@octokit/rest": "22.0.1",
|
||||
"@rsdoctor/rspack-plugin": "1.5.12",
|
||||
"@rspack/core": "2.0.6",
|
||||
"@rspack/core": "2.0.5",
|
||||
"@rspack/dev-server": "2.0.3",
|
||||
"@types/chromecast-caf-receiver": "6.0.26",
|
||||
"@types/chromecast-caf-sender": "1.0.11",
|
||||
@@ -190,11 +190,11 @@
|
||||
"rspack-manifest-plugin": "5.2.1",
|
||||
"serve": "14.2.6",
|
||||
"sinon": "22.0.0",
|
||||
"tar": "7.5.16",
|
||||
"tar": "7.5.15",
|
||||
"terser-webpack-plugin": "5.6.1",
|
||||
"ts-lit-plugin": "2.0.2",
|
||||
"typescript": "6.0.3",
|
||||
"typescript-eslint": "8.60.1",
|
||||
"typescript-eslint": "8.60.0",
|
||||
"vite-tsconfig-paths": "6.1.1",
|
||||
"vitest": "4.1.8",
|
||||
"webpack-stats-plugin": "1.1.3",
|
||||
@@ -212,7 +212,7 @@
|
||||
"@material/mwc-list@^0.27.0": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch",
|
||||
"glob@^10.2.2": "^10.5.0"
|
||||
},
|
||||
"packageManager": "yarn@4.16.0",
|
||||
"packageManager": "yarn@4.15.0",
|
||||
"volta": {
|
||||
"node": "24.16.0"
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import type { HassEntities, HassEntity } from "home-assistant-js-websocket";
|
||||
import { computeStateDomain } from "./compute_state_domain";
|
||||
|
||||
export interface EntityLocation {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
gpsAccuracy?: number;
|
||||
}
|
||||
|
||||
const findFirstActiveZone = (
|
||||
inZones: readonly string[],
|
||||
states: HassEntities
|
||||
): HassEntity | undefined => {
|
||||
for (const zoneId of inZones) {
|
||||
const zone = states[zoneId];
|
||||
if (
|
||||
zone &&
|
||||
!zone.attributes.passive &&
|
||||
typeof zone.attributes.latitude === "number" &&
|
||||
typeof zone.attributes.longitude === "number"
|
||||
) {
|
||||
return zone;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const getEntityLocation = (
|
||||
stateObj: HassEntity,
|
||||
states: HassEntities
|
||||
): EntityLocation | undefined => {
|
||||
const {
|
||||
latitude,
|
||||
longitude,
|
||||
gps_accuracy: gpsAccuracy,
|
||||
} = stateObj.attributes;
|
||||
if (typeof latitude === "number" && typeof longitude === "number") {
|
||||
return { latitude, longitude, gpsAccuracy };
|
||||
}
|
||||
|
||||
if (computeStateDomain(stateObj) !== "person") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const inZones = stateObj.attributes.in_zones;
|
||||
if (!Array.isArray(inZones) || inZones.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const zone = findFirstActiveZone(inZones, states);
|
||||
if (!zone) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
latitude: zone.attributes.latitude,
|
||||
longitude: zone.attributes.longitude,
|
||||
};
|
||||
};
|
||||
@@ -2,17 +2,12 @@ import { mdiDragHorizontalVariant } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import {
|
||||
fireEvent,
|
||||
type HASSDomCurrentTargetEvent,
|
||||
type HASSDomEvent,
|
||||
} from "../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { isValidEntityId } from "../../common/entity/valid_entity_id";
|
||||
import type { HaEntityPickerEntityFilterFunc } from "../../data/entity/entity";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../../types";
|
||||
import "../ha-sortable";
|
||||
import "./ha-entity-picker";
|
||||
import type { HaEntityPicker } from "./ha-entity-picker";
|
||||
|
||||
@customElement("ha-entities-picker")
|
||||
class HaEntitiesPicker extends LitElement {
|
||||
@@ -156,7 +151,7 @@ class HaEntitiesPicker extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _entityMoved(e: HASSDomEvent<HASSDomEvents["item-moved"]>) {
|
||||
private _entityMoved(e: CustomEvent) {
|
||||
e.stopPropagation();
|
||||
const { oldIndex, newIndex } = e.detail;
|
||||
const currentEntities = this._currentEntities;
|
||||
@@ -183,7 +178,7 @@ class HaEntitiesPicker extends LitElement {
|
||||
return this.value || [];
|
||||
}
|
||||
|
||||
private async _updateEntities(entities: string[]) {
|
||||
private async _updateEntities(entities) {
|
||||
this.value = entities;
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
@@ -191,12 +186,9 @@ class HaEntitiesPicker extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _entityChanged(
|
||||
event: ValueChangedEvent<string> &
|
||||
HASSDomCurrentTargetEvent<HaEntityPicker & { curValue: string }>
|
||||
) {
|
||||
private _entityChanged(event: ValueChangedEvent<string>) {
|
||||
event.stopPropagation();
|
||||
const curValue = event.currentTarget.curValue;
|
||||
const curValue = (event.currentTarget as any).curValue;
|
||||
const newValue = event.detail.value;
|
||||
if (
|
||||
newValue === curValue ||
|
||||
@@ -214,15 +206,13 @@ class HaEntitiesPicker extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
private _addEntity(
|
||||
event: ValueChangedEvent<string> & HASSDomCurrentTargetEvent<HaEntityPicker>
|
||||
) {
|
||||
private async _addEntity(event: ValueChangedEvent<string>) {
|
||||
event.stopPropagation();
|
||||
const toAdd = event.detail.value;
|
||||
if (!toAdd) {
|
||||
return;
|
||||
}
|
||||
event.currentTarget.value = "";
|
||||
(event.currentTarget as any).value = "";
|
||||
if (!toAdd) {
|
||||
return;
|
||||
}
|
||||
@@ -249,7 +239,6 @@ class HaEntitiesPicker extends LitElement {
|
||||
}
|
||||
.entity ha-entity-picker {
|
||||
flex: 1;
|
||||
min-width: var(--ha-entities-picker-entity-min-width, auto);
|
||||
}
|
||||
.entity-handle {
|
||||
padding: 8px;
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { consumeLocalize } from "../common/decorators/consume-context-entry";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./input/ha-input-multi";
|
||||
|
||||
@customElement("ha-aliases-editor")
|
||||
class AliasesEditor extends LitElement {
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Array }) public aliases!: string[];
|
||||
|
||||
@@ -28,9 +25,9 @@ class AliasesEditor extends LitElement {
|
||||
.disabled=${this.disabled}
|
||||
.sortable=${this.sortable}
|
||||
update-on-blur
|
||||
.label=${this._localize("ui.dialogs.aliases.label")}
|
||||
.removeLabel=${this._localize("ui.dialogs.aliases.remove")}
|
||||
.addLabel=${this._localize("ui.dialogs.aliases.add")}
|
||||
.label=${this.hass!.localize("ui.dialogs.aliases.label")}
|
||||
.removeLabel=${this.hass!.localize("ui.dialogs.aliases.remove")}
|
||||
.addLabel=${this.hass!.localize("ui.dialogs.aliases.add")}
|
||||
item-index
|
||||
@value-changed=${this._aliasesChanged}
|
||||
>
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { ContextType } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { until } from "lit/directives/until";
|
||||
import { formattersContext } from "../data/context";
|
||||
import type { HomeAssistant } from "../types";
|
||||
|
||||
@customElement("ha-attribute-value")
|
||||
class HaAttributeValue extends LitElement {
|
||||
@state()
|
||||
@consume({ context: formattersContext, subscribe: true })
|
||||
private _formatters?: ContextType<typeof formattersContext>;
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
|
||||
@@ -57,7 +53,7 @@ class HaAttributeValue extends LitElement {
|
||||
}
|
||||
|
||||
if (this.hideUnit) {
|
||||
const parts = this._formatters!.formatEntityAttributeValueToParts(
|
||||
const parts = this.hass.formatEntityAttributeValueToParts(
|
||||
this.stateObj!,
|
||||
this.attribute
|
||||
);
|
||||
@@ -67,10 +63,7 @@ class HaAttributeValue extends LitElement {
|
||||
.join("");
|
||||
}
|
||||
|
||||
return this._formatters!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
this.attribute
|
||||
);
|
||||
return this.hass.formatEntityAttributeValue(this.stateObj!, this.attribute);
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
|
||||
@@ -80,6 +80,7 @@ class HaAttributes extends LitElement {
|
||||
</div>
|
||||
<div class="value">
|
||||
<ha-attribute-value
|
||||
.hass=${this.hass}
|
||||
.attribute=${attribute}
|
||||
.stateObj=${this.stateObj}
|
||||
></ha-attribute-value>
|
||||
|
||||
@@ -1,22 +1,14 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { ContextType } from "@lit/context";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { consumeLocalize } from "../common/decorators/consume-context-entry";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import type { ClimateEntity } from "../data/climate";
|
||||
import { CLIMATE_PRESET_NONE } from "../data/climate";
|
||||
import { formattersContext } from "../data/context";
|
||||
import { OFF, UNAVAILABLE, UNKNOWN } from "../data/entity/entity";
|
||||
import type { HomeAssistant } from "../types";
|
||||
|
||||
@customElement("ha-climate-state")
|
||||
class HaClimateState extends LitElement {
|
||||
@state()
|
||||
@consume({ context: formattersContext, subscribe: true })
|
||||
private _formatters?: ContextType<typeof formattersContext>;
|
||||
|
||||
@state() @consumeLocalize() private _localize!: LocalizeFunc;
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj!: ClimateEntity;
|
||||
|
||||
@@ -32,7 +24,7 @@ class HaClimateState extends LitElement {
|
||||
${this.stateObj.attributes.preset_mode &&
|
||||
this.stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
|
||||
? html`-
|
||||
${this._formatters!.formatEntityAttributeValue(
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"preset_mode"
|
||||
)}`
|
||||
@@ -45,7 +37,7 @@ class HaClimateState extends LitElement {
|
||||
${currentStatus && !noValue
|
||||
? html`
|
||||
<div class="current">
|
||||
${this._localize("ui.card.climate.currently")}:
|
||||
${this.hass.localize("ui.card.climate.currently")}:
|
||||
<div class="unit">${currentStatus}</div>
|
||||
</div>
|
||||
`
|
||||
@@ -53,32 +45,32 @@ class HaClimateState extends LitElement {
|
||||
}
|
||||
|
||||
private _computeCurrentStatus(): string | undefined {
|
||||
if (!this._formatters || !this.stateObj) {
|
||||
if (!this.hass || !this.stateObj) {
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
this.stateObj.attributes.current_temperature != null &&
|
||||
this.stateObj.attributes.current_humidity != null
|
||||
) {
|
||||
return `${this._formatters.formatEntityAttributeValue(
|
||||
return `${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"current_temperature"
|
||||
)}/
|
||||
${this._formatters.formatEntityAttributeValue(
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"current_humidity"
|
||||
)}`;
|
||||
}
|
||||
|
||||
if (this.stateObj.attributes.current_temperature != null) {
|
||||
return this._formatters.formatEntityAttributeValue(
|
||||
return this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"current_temperature"
|
||||
);
|
||||
}
|
||||
|
||||
if (this.stateObj.attributes.current_humidity != null) {
|
||||
return this._formatters.formatEntityAttributeValue(
|
||||
return this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"current_humidity"
|
||||
);
|
||||
@@ -88,7 +80,7 @@ class HaClimateState extends LitElement {
|
||||
}
|
||||
|
||||
private _computeTarget(): string {
|
||||
if (!this._formatters || !this.stateObj) {
|
||||
if (!this.hass || !this.stateObj) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -96,39 +88,33 @@ class HaClimateState extends LitElement {
|
||||
this.stateObj.attributes.target_temp_low != null &&
|
||||
this.stateObj.attributes.target_temp_high != null
|
||||
) {
|
||||
return `${this._formatters.formatEntityAttributeValue(
|
||||
return `${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"target_temp_low"
|
||||
)}-${this._formatters.formatEntityAttributeValue(
|
||||
)}-${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"target_temp_high"
|
||||
)}`;
|
||||
}
|
||||
|
||||
if (this.stateObj.attributes.temperature != null) {
|
||||
return this._formatters.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"temperature"
|
||||
);
|
||||
return this.hass.formatEntityAttributeValue(this.stateObj, "temperature");
|
||||
}
|
||||
if (
|
||||
this.stateObj.attributes.target_humidity_low != null &&
|
||||
this.stateObj.attributes.target_humidity_high != null
|
||||
) {
|
||||
return `${this._formatters.formatEntityAttributeValue(
|
||||
return `${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"target_humidity_low"
|
||||
)}-${this._formatters.formatEntityAttributeValue(
|
||||
)}-${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"target_humidity_high"
|
||||
)}`;
|
||||
}
|
||||
|
||||
if (this.stateObj.attributes.humidity != null) {
|
||||
return this._formatters.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"humidity"
|
||||
);
|
||||
return this.hass.formatEntityAttributeValue(this.stateObj, "humidity");
|
||||
}
|
||||
|
||||
return "";
|
||||
@@ -139,13 +125,13 @@ class HaClimateState extends LitElement {
|
||||
this.stateObj.state === UNAVAILABLE ||
|
||||
this.stateObj.state === UNKNOWN
|
||||
) {
|
||||
return this._localize(`state.default.${this.stateObj.state}`);
|
||||
return this.hass.localize(`state.default.${this.stateObj.state}`);
|
||||
}
|
||||
|
||||
const stateString = this._formatters!.formatEntityState(this.stateObj);
|
||||
const stateString = this.hass.formatEntityState(this.stateObj);
|
||||
|
||||
if (this.stateObj.attributes.hvac_action && this.stateObj.state !== OFF) {
|
||||
const actionString = this._formatters!.formatEntityAttributeValue(
|
||||
const actionString = this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"hvac_action"
|
||||
);
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { mdiStop } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { consumeLocalize } from "../common/decorators/consume-context-entry";
|
||||
import { computeCloseIcon, computeOpenIcon } from "../common/entity/cover_icon";
|
||||
import { supportsFeature } from "../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import { apiContext } from "../data/context";
|
||||
import type { CoverEntity } from "../data/cover";
|
||||
import { canClose, canOpen, canStop, CoverEntityFeature } from "../data/cover";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-icon-button";
|
||||
|
||||
@customElement("ha-cover-controls")
|
||||
class HaCoverControls extends LitElement {
|
||||
@state() @consumeLocalize() private _localize!: LocalizeFunc;
|
||||
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: ContextType<typeof apiContext>;
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj!: CoverEntity;
|
||||
|
||||
@@ -32,7 +26,7 @@ class HaCoverControls extends LitElement {
|
||||
class=${classMap({
|
||||
hidden: !supportsFeature(this.stateObj, CoverEntityFeature.OPEN),
|
||||
})}
|
||||
.label=${this._localize("ui.card.cover.open_cover")}
|
||||
.label=${this.hass.localize("ui.card.cover.open_cover")}
|
||||
@click=${this._onOpenTap}
|
||||
.disabled=${!canOpen(this.stateObj)}
|
||||
.path=${computeOpenIcon(this.stateObj)}
|
||||
@@ -42,7 +36,7 @@ class HaCoverControls extends LitElement {
|
||||
class=${classMap({
|
||||
hidden: !supportsFeature(this.stateObj, CoverEntityFeature.STOP),
|
||||
})}
|
||||
.label=${this._localize("ui.card.cover.stop_cover")}
|
||||
.label=${this.hass.localize("ui.card.cover.stop_cover")}
|
||||
.path=${mdiStop}
|
||||
@click=${this._onStopTap}
|
||||
.disabled=${!canStop(this.stateObj)}
|
||||
@@ -51,7 +45,7 @@ class HaCoverControls extends LitElement {
|
||||
class=${classMap({
|
||||
hidden: !supportsFeature(this.stateObj, CoverEntityFeature.CLOSE),
|
||||
})}
|
||||
.label=${this._localize("ui.card.cover.close_cover")}
|
||||
.label=${this.hass.localize("ui.card.cover.close_cover")}
|
||||
@click=${this._onCloseTap}
|
||||
.disabled=${!canClose(this.stateObj)}
|
||||
.path=${computeCloseIcon(this.stateObj)}
|
||||
@@ -63,21 +57,21 @@ class HaCoverControls extends LitElement {
|
||||
|
||||
private _onOpenTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this._api.callService("cover", "open_cover", {
|
||||
this.hass.callService("cover", "open_cover", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _onCloseTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this._api.callService("cover", "close_cover", {
|
||||
this.hass.callService("cover", "close_cover", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _onStopTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this._api.callService("cover", "stop_cover", {
|
||||
this.hass.callService("cover", "stop_cover", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { mdiArrowBottomLeft, mdiArrowTopRight, mdiStop } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { consumeLocalize } from "../common/decorators/consume-context-entry";
|
||||
import { supportsFeature } from "../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import { apiContext } from "../data/context";
|
||||
import type { CoverEntity } from "../data/cover";
|
||||
import {
|
||||
canCloseTilt,
|
||||
@@ -14,14 +10,12 @@ import {
|
||||
canStopTilt,
|
||||
CoverEntityFeature,
|
||||
} from "../data/cover";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-icon-button";
|
||||
|
||||
@customElement("ha-cover-tilt-controls")
|
||||
class HaCoverTiltControls extends LitElement {
|
||||
@state() @consumeLocalize() private _localize!: LocalizeFunc;
|
||||
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: ContextType<typeof apiContext>;
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) stateObj!: CoverEntity;
|
||||
|
||||
@@ -37,7 +31,7 @@ class HaCoverTiltControls extends LitElement {
|
||||
CoverEntityFeature.OPEN_TILT
|
||||
),
|
||||
})}
|
||||
.label=${this._localize("ui.card.cover.open_tilt_cover")}
|
||||
.label=${this.hass.localize("ui.card.cover.open_tilt_cover")}
|
||||
.path=${mdiArrowTopRight}
|
||||
@click=${this._onOpenTiltTap}
|
||||
.disabled=${!canOpenTilt(this.stateObj)}
|
||||
@@ -49,7 +43,7 @@ class HaCoverTiltControls extends LitElement {
|
||||
CoverEntityFeature.STOP_TILT
|
||||
),
|
||||
})}
|
||||
.label=${this._localize("ui.card.cover.stop_cover")}
|
||||
.label=${this.hass.localize("ui.card.cover.stop_cover")}
|
||||
.path=${mdiStop}
|
||||
@click=${this._onStopTiltTap}
|
||||
.disabled=${!canStopTilt(this.stateObj)}
|
||||
@@ -61,7 +55,7 @@ class HaCoverTiltControls extends LitElement {
|
||||
CoverEntityFeature.CLOSE_TILT
|
||||
),
|
||||
})}
|
||||
.label=${this._localize("ui.card.cover.close_tilt_cover")}
|
||||
.label=${this.hass.localize("ui.card.cover.close_tilt_cover")}
|
||||
.path=${mdiArrowBottomLeft}
|
||||
@click=${this._onCloseTiltTap}
|
||||
.disabled=${!canCloseTilt(this.stateObj)}
|
||||
@@ -70,21 +64,21 @@ class HaCoverTiltControls extends LitElement {
|
||||
|
||||
private _onOpenTiltTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this._api.callService("cover", "open_cover_tilt", {
|
||||
this.hass.callService("cover", "open_cover_tilt", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _onCloseTiltTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this._api.callService("cover", "close_cover_tilt", {
|
||||
this.hass.callService("cover", "close_cover_tilt", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _onStopTiltTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this._api.callService("cover", "stop_cover_tilt", {
|
||||
this.hass.callService("cover", "stop_cover_tilt", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
import type { SelectedDetail } from "@material/mwc-list";
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { mdiFilterVariantRemove } from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { consumeLocalize } from "../common/decorators/consume-context-entry";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stringCompare } from "../common/string/compare";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import { internationalizationContext, manifestsContext } from "../data/context";
|
||||
import type { IntegrationManifest } from "../data/integration";
|
||||
import { domainToName } from "../data/integration";
|
||||
import { domainToName, fetchIntegrationManifests } from "../data/integration";
|
||||
import { haStyleScrollbar } from "../resources/styles";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-check-list-item";
|
||||
import "./ha-domain-icon";
|
||||
import "./ha-expansion-panel";
|
||||
@@ -23,26 +21,15 @@ import type { HaInputSearch } from "./input/ha-input-search";
|
||||
|
||||
@customElement("ha-filter-integrations")
|
||||
export class HaFilterIntegrations extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public value?: string[];
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public expanded = false;
|
||||
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@consume({ context: manifestsContext, subscribe: true })
|
||||
@state()
|
||||
private _manifests?: ContextType<typeof manifestsContext>;
|
||||
|
||||
private _manifestList = memoizeOne(
|
||||
(manifests: ContextType<typeof manifestsContext>) =>
|
||||
Object.values(manifests)
|
||||
);
|
||||
@state() private _manifests?: IntegrationManifest[];
|
||||
|
||||
@state() private _shouldRender = false;
|
||||
|
||||
@@ -51,10 +38,6 @@ export class HaFilterIntegrations extends LitElement {
|
||||
@query("ha-list") private _list?: HTMLElement;
|
||||
|
||||
protected render() {
|
||||
const manifests = this._manifests
|
||||
? this._manifestList(this._manifests)
|
||||
: undefined;
|
||||
|
||||
return html`
|
||||
<ha-expansion-panel
|
||||
left-chevron
|
||||
@@ -63,7 +46,7 @@ export class HaFilterIntegrations extends LitElement {
|
||||
@expanded-changed=${this._expandedChanged}
|
||||
>
|
||||
<div slot="header" class="header">
|
||||
${this._localize("ui.panel.config.integrations.caption")}
|
||||
${this.hass.localize("ui.panel.config.integrations.caption")}
|
||||
${this.value?.length
|
||||
? html`<div class="badge">${this.value?.length}</div>
|
||||
<ha-icon-button
|
||||
@@ -72,7 +55,7 @@ export class HaFilterIntegrations extends LitElement {
|
||||
></ha-icon-button>`
|
||||
: nothing}
|
||||
</div>
|
||||
${manifests && this._shouldRender
|
||||
${this._manifests && this._shouldRender
|
||||
? html`<ha-input-search
|
||||
appearance="outlined"
|
||||
.value=${this._filter}
|
||||
@@ -86,11 +69,10 @@ export class HaFilterIntegrations extends LitElement {
|
||||
>
|
||||
${repeat(
|
||||
this._integrations(
|
||||
this._localize,
|
||||
manifests,
|
||||
this.hass.localize,
|
||||
this._manifests,
|
||||
this._filter,
|
||||
this.value,
|
||||
this._i18n.locale.language
|
||||
this.value
|
||||
),
|
||||
(i) => i.domain,
|
||||
(integration) =>
|
||||
@@ -135,8 +117,9 @@ export class HaFilterIntegrations extends LitElement {
|
||||
this.expanded = ev.detail.expanded;
|
||||
}
|
||||
|
||||
protected firstUpdated() {
|
||||
this._i18n.loadBackendTranslation("title");
|
||||
protected async firstUpdated() {
|
||||
this._manifests = await fetchIntegrationManifests(this.hass);
|
||||
this.hass.loadBackendTranslation("title");
|
||||
}
|
||||
|
||||
private _integrations = memoizeOne(
|
||||
@@ -144,8 +127,7 @@ export class HaFilterIntegrations extends LitElement {
|
||||
localize: LocalizeFunc,
|
||||
manifest: IntegrationManifest[],
|
||||
filter: string | undefined,
|
||||
_value: string[] | undefined,
|
||||
language: string
|
||||
_value
|
||||
) =>
|
||||
manifest
|
||||
.map((mnfst) => ({
|
||||
@@ -162,20 +144,17 @@ export class HaFilterIntegrations extends LitElement {
|
||||
mnfst.name.toLowerCase().includes(filter) ||
|
||||
mnfst.domain.toLowerCase().includes(filter))
|
||||
)
|
||||
.sort((a, b) => stringCompare(a.name, b.name, language))
|
||||
.sort((a, b) =>
|
||||
stringCompare(a.name, b.name, this.hass.locale.language)
|
||||
)
|
||||
);
|
||||
|
||||
private _itemSelected(ev: CustomEvent<SelectedDetail<Set<number>>>) {
|
||||
if (!this._manifests) {
|
||||
return;
|
||||
}
|
||||
|
||||
const integrations = this._integrations(
|
||||
this._localize,
|
||||
this._manifestList(this._manifests),
|
||||
this.hass.localize,
|
||||
this._manifests!,
|
||||
this._filter,
|
||||
this.value,
|
||||
this._i18n.locale.language
|
||||
this.value
|
||||
);
|
||||
|
||||
const visibleDomains = new Set(integrations.map((i) => i.domain));
|
||||
|
||||
@@ -8,12 +8,13 @@ import { conditionalClamp } from "../common/number/clamp";
|
||||
import type { CardGridSize } from "../panels/lovelace/common/compute-card-grid-size";
|
||||
import { DEFAULT_GRID_SIZE } from "../panels/lovelace/common/compute-card-grid-size";
|
||||
import "../panels/lovelace/editor/card-editor/ha-grid-layout-slider";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-icon-button";
|
||||
import { consumeLocalize } from "../common/decorators/consume-context-entry";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
|
||||
@customElement("ha-grid-size-picker")
|
||||
export class HaGridSizeEditor extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public value?: CardGridSize;
|
||||
|
||||
@property({ attribute: false }) public rows = 8;
|
||||
@@ -34,10 +35,6 @@ export class HaGridSizeEditor extends LitElement {
|
||||
|
||||
@state() public _localValue?: CardGridSize = { rows: 1, columns: 1 };
|
||||
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
protected willUpdate(changedProperties: PropertyValues<this>) {
|
||||
if (changedProperties.has("value")) {
|
||||
this._localValue = this.value;
|
||||
@@ -65,7 +62,9 @@ export class HaGridSizeEditor extends LitElement {
|
||||
return html`
|
||||
<div class="grid">
|
||||
<ha-grid-layout-slider
|
||||
aria-label=${this._localize("ui.components.grid-size-picker.columns")}
|
||||
aria-label=${this.hass.localize(
|
||||
"ui.components.grid-size-picker.columns"
|
||||
)}
|
||||
id="columns"
|
||||
.min=${columnMin}
|
||||
.max=${columnMax}
|
||||
@@ -79,7 +78,9 @@ export class HaGridSizeEditor extends LitElement {
|
||||
></ha-grid-layout-slider>
|
||||
|
||||
<ha-grid-layout-slider
|
||||
aria-label=${this._localize("ui.components.grid-size-picker.rows")}
|
||||
aria-label=${this.hass.localize(
|
||||
"ui.components.grid-size-picker.rows"
|
||||
)}
|
||||
id="rows"
|
||||
.min=${rowMin}
|
||||
.max=${rowMax}
|
||||
@@ -97,10 +98,10 @@ export class HaGridSizeEditor extends LitElement {
|
||||
@click=${this._reset}
|
||||
class="reset"
|
||||
.path=${mdiRestore}
|
||||
label=${this._localize(
|
||||
label=${this.hass.localize(
|
||||
"ui.components.grid-size-picker.reset_default"
|
||||
)}
|
||||
title=${this._localize(
|
||||
title=${this.hass.localize(
|
||||
"ui.components.grid-size-picker.reset_default"
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { ContextType } from "@lit/context";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { consumeLocalize } from "../common/decorators/consume-context-entry";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import { formattersContext } from "../data/context";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { OFF, UNAVAILABLE, UNKNOWN } from "../data/entity/entity";
|
||||
import type { HumidifierEntity } from "../data/humidifier";
|
||||
import type { HomeAssistant } from "../types";
|
||||
|
||||
@customElement("ha-humidifier-state")
|
||||
class HaHumidifierState extends LitElement {
|
||||
@state()
|
||||
@consume({ context: formattersContext, subscribe: true })
|
||||
private _formatters?: ContextType<typeof formattersContext>;
|
||||
|
||||
@state() @consumeLocalize() private _localize!: LocalizeFunc;
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj!: HumidifierEntity;
|
||||
|
||||
@@ -30,7 +22,7 @@ class HaHumidifierState extends LitElement {
|
||||
${this._localizeState()}
|
||||
${this.stateObj.attributes.mode
|
||||
? html`-
|
||||
${this._formatters!.formatEntityAttributeValue(
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"mode"
|
||||
)}`
|
||||
@@ -42,19 +34,19 @@ class HaHumidifierState extends LitElement {
|
||||
|
||||
${currentStatus && !noValue
|
||||
? html`<div class="current">
|
||||
${this._localize("ui.card.humidifier.currently")}:
|
||||
${this.hass.localize("ui.card.humidifier.currently")}:
|
||||
<div class="unit">${currentStatus}</div>
|
||||
</div>`
|
||||
: ""}`;
|
||||
}
|
||||
|
||||
private _computeCurrentStatus(): string | undefined {
|
||||
if (!this._formatters || !this.stateObj) {
|
||||
if (!this.hass || !this.stateObj) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (this.stateObj.attributes.current_humidity != null) {
|
||||
return `${this._formatters.formatEntityAttributeValue(
|
||||
return `${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"current_humidity"
|
||||
)}`;
|
||||
@@ -64,12 +56,12 @@ class HaHumidifierState extends LitElement {
|
||||
}
|
||||
|
||||
private _computeTarget(): string {
|
||||
if (!this._formatters || !this.stateObj) {
|
||||
if (!this.hass || !this.stateObj) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (this.stateObj.attributes.humidity != null) {
|
||||
return `${this._formatters.formatEntityAttributeValue(
|
||||
return `${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"humidity"
|
||||
)}`;
|
||||
@@ -83,13 +75,13 @@ class HaHumidifierState extends LitElement {
|
||||
this.stateObj.state === UNAVAILABLE ||
|
||||
this.stateObj.state === UNKNOWN
|
||||
) {
|
||||
return this._localize(`state.default.${this.stateObj.state}`);
|
||||
return this.hass.localize(`state.default.${this.stateObj.state}`);
|
||||
}
|
||||
|
||||
const stateString = this._formatters!.formatEntityState(this.stateObj);
|
||||
const stateString = this.hass.formatEntityState(this.stateObj);
|
||||
|
||||
if (this.stateObj.attributes.action && this.stateObj.state !== OFF) {
|
||||
const actionString = this._formatters!.formatEntityAttributeValue(
|
||||
const actionString = this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"action"
|
||||
);
|
||||
|
||||
@@ -3,12 +3,13 @@ import type { TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { mainWindow } from "../common/dom/get_main_window";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-icon-button";
|
||||
import { consumeLocalize } from "../common/decorators/consume-context-entry";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
|
||||
@customElement("ha-icon-button-arrow-next")
|
||||
export class HaIconButtonArrowNext extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property() public label?: string;
|
||||
@@ -16,15 +17,11 @@ export class HaIconButtonArrowNext extends LitElement {
|
||||
@state() private _icon =
|
||||
mainWindow.document.dir === "rtl" ? mdiArrowLeft : mdiArrowRight;
|
||||
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-icon-button
|
||||
.disabled=${this.disabled}
|
||||
.label=${this.label || this._localize("ui.common.next") || "Next"}
|
||||
.label=${this.label || this.hass?.localize("ui.common.next") || "Next"}
|
||||
.path=${this._icon}
|
||||
></ha-icon-button>
|
||||
`;
|
||||
|
||||
@@ -3,12 +3,13 @@ import type { TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { mainWindow } from "../common/dom/get_main_window";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-icon-button";
|
||||
import { consumeLocalize } from "../common/decorators/consume-context-entry";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
|
||||
@customElement("ha-icon-button-next")
|
||||
export class HaIconButtonNext extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property() public label?: string;
|
||||
@@ -24,15 +25,11 @@ export class HaIconButtonNext extends LitElement {
|
||||
@state() private _icon =
|
||||
mainWindow.document.dir === "rtl" ? mdiChevronLeft : mdiChevronRight;
|
||||
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-icon-button
|
||||
.disabled=${this.disabled}
|
||||
.label=${this.label || this._localize("ui.common.next") || "Next"}
|
||||
.label=${this.label || this.hass?.localize("ui.common.next") || "Next"}
|
||||
.path=${this._icon}
|
||||
.href=${this.href}
|
||||
.target=${this.target}
|
||||
|
||||
@@ -3,12 +3,13 @@ import type { TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { mainWindow } from "../common/dom/get_main_window";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-icon-button";
|
||||
import { consumeLocalize } from "../common/decorators/consume-context-entry";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
|
||||
@customElement("ha-icon-button-prev")
|
||||
export class HaIconButtonPrev extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property() public label?: string;
|
||||
@@ -24,15 +25,11 @@ export class HaIconButtonPrev extends LitElement {
|
||||
@state() private _icon =
|
||||
mainWindow.document.dir === "rtl" ? mdiChevronRight : mdiChevronLeft;
|
||||
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-icon-button
|
||||
.disabled=${this.disabled}
|
||||
.label=${this.label || this._localize("ui.common.back") || "Back"}
|
||||
.label=${this.label || this.hass?.localize("ui.common.back") || "Back"}
|
||||
.path=${this._icon}
|
||||
.href=${this.href}
|
||||
.target=${this.target}
|
||||
|
||||
@@ -1,36 +1,18 @@
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { mdiMenu } from "@mdi/js";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import {
|
||||
connectionContext,
|
||||
narrowViewportContext,
|
||||
uiContext,
|
||||
} from "../data/context";
|
||||
import { subscribeNotifications } from "../data/persistent_notification";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-icon-button";
|
||||
import { consumeLocalize } from "../common/decorators/consume-context-entry";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
|
||||
@customElement("ha-menu-button")
|
||||
class HaMenuButton extends LitElement {
|
||||
@state()
|
||||
@consume({ context: connectionContext, subscribe: true })
|
||||
private _connection?: ContextType<typeof connectionContext>;
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: narrowViewportContext, subscribe: true })
|
||||
private _narrow = false;
|
||||
|
||||
@state()
|
||||
@consume({ context: uiContext, subscribe: true })
|
||||
private _ui?: ContextType<typeof uiContext>;
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _hasNotifications = false;
|
||||
|
||||
@@ -44,7 +26,7 @@ class HaMenuButton extends LitElement {
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
if (this._attachNotifOnConnect && this._connection) {
|
||||
if (this._attachNotifOnConnect) {
|
||||
this._attachNotifOnConnect = false;
|
||||
this._subscribeNotifications();
|
||||
}
|
||||
@@ -60,15 +42,15 @@ class HaMenuButton extends LitElement {
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._show || !this._ui) {
|
||||
if (!this._show) {
|
||||
return nothing;
|
||||
}
|
||||
const hasNotifications =
|
||||
this._hasNotifications &&
|
||||
(this._narrow || this._ui.dockedSidebar === "always_hidden");
|
||||
(this.narrow || this.hass.dockedSidebar === "always_hidden");
|
||||
return html`
|
||||
<ha-icon-button
|
||||
.label=${this._localize("ui.sidebar.sidebar_toggle")}
|
||||
.label=${this.hass.localize("ui.sidebar.sidebar_toggle")}
|
||||
.path=${mdiMenu}
|
||||
@click=${this._toggleMenu}
|
||||
></ha-icon-button>
|
||||
@@ -76,25 +58,30 @@ class HaMenuButton extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues) {
|
||||
protected willUpdate(changedProps: PropertyValues<this>) {
|
||||
super.willUpdate(changedProps);
|
||||
|
||||
if (
|
||||
!changedProps.has("_narrow") &&
|
||||
!changedProps.has("_ui") &&
|
||||
!changedProps.has("_connection")
|
||||
) {
|
||||
if (!changedProps.has("narrow") && !changedProps.has("hass")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (changedProps.has("_connection") && this._unsubNotifications) {
|
||||
this._unsubNotifications();
|
||||
this._unsubNotifications = undefined;
|
||||
}
|
||||
const oldHass = changedProps.has("hass")
|
||||
? (changedProps.get("hass") as HomeAssistant | undefined)
|
||||
: this.hass;
|
||||
const oldNarrow = changedProps.has("narrow")
|
||||
? (changedProps.get("narrow") as boolean | undefined)
|
||||
: this.narrow;
|
||||
|
||||
const oldShowButton =
|
||||
oldHass?.kioskMode === false &&
|
||||
(oldNarrow || oldHass?.dockedSidebar === "always_hidden");
|
||||
const showButton =
|
||||
this._ui?.kioskMode === false &&
|
||||
(this._narrow || this._ui.dockedSidebar === "always_hidden");
|
||||
this.hass.kioskMode === false &&
|
||||
(this.narrow || this.hass.dockedSidebar === "always_hidden");
|
||||
|
||||
if (this.hasUpdated && oldShowButton === showButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._show = showButton || this._alwaysVisible;
|
||||
|
||||
@@ -106,10 +93,7 @@ class HaMenuButton extends LitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._unsubNotifications && this._connection) {
|
||||
this._attachNotifOnConnect = false;
|
||||
this._subscribeNotifications();
|
||||
}
|
||||
this._subscribeNotifications();
|
||||
}
|
||||
|
||||
private _subscribeNotifications() {
|
||||
@@ -117,7 +101,7 @@ class HaMenuButton extends LitElement {
|
||||
throw new Error("Already subscribed");
|
||||
}
|
||||
this._unsubNotifications = subscribeNotifications(
|
||||
this._connection!.connection,
|
||||
this.hass.connection,
|
||||
(notifications) => {
|
||||
this._hasNotifications = notifications.length > 0;
|
||||
}
|
||||
|
||||
@@ -10,9 +10,8 @@ import type {
|
||||
NetworkConfig,
|
||||
} from "../data/network";
|
||||
import { haStyle } from "../resources/styles";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-checkbox";
|
||||
import { consumeLocalize } from "../common/decorators/consume-context-entry";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import type { HaCheckbox } from "./ha-checkbox";
|
||||
import "./ha-settings-row";
|
||||
import "./ha-svg-icon";
|
||||
@@ -42,14 +41,12 @@ declare global {
|
||||
}
|
||||
@customElement("ha-network")
|
||||
export class HaNetwork extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public networkConfig?: NetworkConfig;
|
||||
|
||||
@state() private _expanded?: boolean;
|
||||
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
protected render() {
|
||||
if (this.networkConfig === undefined) {
|
||||
return nothing;
|
||||
@@ -60,14 +57,14 @@ export class HaNetwork extends LitElement {
|
||||
@change=${this._handleAutoConfigureCheckboxClick}
|
||||
.checked=${!configured_adapters.length}
|
||||
.hint=${!configured_adapters.length
|
||||
? this._localize(
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.network.adapter.auto_configure_manual_hint"
|
||||
)
|
||||
: ""}
|
||||
>
|
||||
${this._localize("ui.panel.config.network.adapter.auto_configure")}
|
||||
${this.hass.localize("ui.panel.config.network.adapter.auto_configure")}
|
||||
<div class="description">
|
||||
${this._localize("ui.panel.config.network.adapter.detected")}:
|
||||
${this.hass.localize("ui.panel.config.network.adapter.detected")}:
|
||||
${format_auto_detected_interfaces(this.networkConfig.adapters)}
|
||||
</div>
|
||||
</ha-checkbox>
|
||||
@@ -80,11 +77,13 @@ export class HaNetwork extends LitElement {
|
||||
.checked=${configured_adapters.includes(adapter.name)}
|
||||
.adapter=${adapter.name}
|
||||
>
|
||||
${this._localize("ui.panel.config.network.adapter.adapter")}:
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.network.adapter.adapter"
|
||||
)}:
|
||||
${adapter.name}
|
||||
${adapter.default
|
||||
? html`<ha-svg-icon .path=${mdiStar}></ha-svg-icon>
|
||||
(${this._localize("ui.common.default")})`
|
||||
(${this.hass.localize("ui.common.default")})`
|
||||
: nothing}
|
||||
<div class="description">
|
||||
${format_addresses([...adapter.ipv4, ...adapter.ipv6])}
|
||||
|
||||
@@ -9,11 +9,7 @@ import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { ensureArray } from "../common/array/ensure-array";
|
||||
import {
|
||||
fireEvent,
|
||||
type HASSDomCurrentTargetEvent,
|
||||
type HASSDomEvent,
|
||||
} from "../common/dom/fire_event";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
import { computeObjectId } from "../common/entity/compute_object_id";
|
||||
import { supportsFeature } from "../common/entity/supports-feature";
|
||||
@@ -36,11 +32,9 @@ import {
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../types";
|
||||
import { documentationUrl } from "../util/documentation-url";
|
||||
import "./ha-checkbox";
|
||||
import type { HaCheckbox } from "./ha-checkbox";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-markdown";
|
||||
import "./ha-selector/ha-selector";
|
||||
import type { HaSelector } from "./ha-selector/ha-selector";
|
||||
import "./ha-service-picker";
|
||||
import "./ha-service-section-icon";
|
||||
import "./ha-settings-row";
|
||||
@@ -62,22 +56,6 @@ const showOptionalToggle = (field) =>
|
||||
!field.required &&
|
||||
!("boolean" in field.selector && field.default);
|
||||
|
||||
const FULL_WIDTH_SELECTOR_TYPES = new Set([
|
||||
"action",
|
||||
"area",
|
||||
"device",
|
||||
"entity",
|
||||
"floor",
|
||||
"label",
|
||||
"target",
|
||||
]);
|
||||
|
||||
const isFullWidthSelector = (selector?: Selector): boolean =>
|
||||
!!selector &&
|
||||
Object.keys(selector).some((selectorType) =>
|
||||
FULL_WIDTH_SELECTOR_TYPES.has(selectorType)
|
||||
);
|
||||
|
||||
interface Field extends Omit<HassService["fields"][string], "selector"> {
|
||||
key: string;
|
||||
selector?: Selector;
|
||||
@@ -497,14 +475,6 @@ export class HaServiceControl extends LitElement {
|
||||
)) ||
|
||||
serviceData?.description;
|
||||
|
||||
const targetSelector =
|
||||
serviceData && "target" in serviceData
|
||||
? this._targetSelector(
|
||||
serviceData.target as TargetSelector,
|
||||
this._value?.target
|
||||
)
|
||||
: undefined;
|
||||
|
||||
return html`${this.hidePicker
|
||||
? nothing
|
||||
: html`<ha-service-picker
|
||||
@@ -542,15 +512,16 @@ export class HaServiceControl extends LitElement {
|
||||
</div>
|
||||
`}
|
||||
${serviceData && "target" in serviceData
|
||||
? html`<ha-settings-row
|
||||
.narrow=${this.narrow || isFullWidthSelector(targetSelector)}
|
||||
>
|
||||
? html`<ha-settings-row .narrow=${this.narrow}>
|
||||
<span slot="heading"
|
||||
>${this.hass.localize("ui.components.service-control.target")}</span
|
||||
>
|
||||
<ha-selector
|
||||
.hass=${this.hass}
|
||||
.selector=${targetSelector}
|
||||
.selector=${this._targetSelector(
|
||||
serviceData.target as TargetSelector,
|
||||
this._value?.target
|
||||
)}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._targetChanged}
|
||||
.value=${this._value?.target}
|
||||
@@ -693,9 +664,7 @@ export class HaServiceControl extends LitElement {
|
||||
: undefined;
|
||||
|
||||
return dataField.selector
|
||||
? html`<ha-settings-row
|
||||
.narrow=${this.narrow || isFullWidthSelector(selector)}
|
||||
>
|
||||
? html`<ha-settings-row .narrow=${this.narrow}>
|
||||
${!showOptional
|
||||
? hasOptional
|
||||
? html`<div slot="prefix" class="checkbox-spacer"></div>`
|
||||
@@ -768,16 +737,14 @@ export class HaServiceControl extends LitElement {
|
||||
);
|
||||
};
|
||||
|
||||
private _toggleCheckbox(ev: HASSDomCurrentTargetEvent<HTMLElement>) {
|
||||
private _toggleCheckbox(ev: Event) {
|
||||
const checkbox = (
|
||||
ev.currentTarget as HTMLElement
|
||||
)?.parentElement?.querySelector("ha-checkbox");
|
||||
checkbox?.click();
|
||||
}
|
||||
|
||||
private _checkboxChanged(
|
||||
ev: HASSDomCurrentTargetEvent<HaCheckbox & { key: string }>
|
||||
) {
|
||||
private _checkboxChanged(ev) {
|
||||
const checked = ev.currentTarget.checked;
|
||||
const key = ev.currentTarget.key;
|
||||
let data;
|
||||
@@ -900,7 +867,7 @@ export class HaServiceControl extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _entityPicked(ev: HASSDomEvent<{ value: string | undefined }>) {
|
||||
private _entityPicked(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const newValue = ev.detail.value;
|
||||
if (this._value?.data?.entity_id === newValue) {
|
||||
@@ -921,12 +888,7 @@ export class HaServiceControl extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _targetChanged(
|
||||
ev: HASSDomEvent<{
|
||||
value: HassServiceTarget | undefined;
|
||||
isValid?: boolean;
|
||||
}>
|
||||
) {
|
||||
private _targetChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
if (ev.detail.isValid === false) {
|
||||
// Don't clear an object selector that returns invalid YAML
|
||||
@@ -948,16 +910,13 @@ export class HaServiceControl extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _serviceDataChanged(
|
||||
ev: HASSDomEvent<{ value: unknown; isValid?: boolean }> &
|
||||
HASSDomCurrentTargetEvent<HaSelector & { key: string }>
|
||||
) {
|
||||
private _serviceDataChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
if (ev.detail.isValid === false) {
|
||||
// Don't clear an object selector that returns invalid YAML
|
||||
return;
|
||||
}
|
||||
const key = ev.currentTarget.key;
|
||||
const key = (ev.currentTarget as any).key;
|
||||
const value = ev.detail.value;
|
||||
if (
|
||||
this._value?.data?.[key] === value ||
|
||||
@@ -972,9 +931,7 @@ export class HaServiceControl extends LitElement {
|
||||
if (
|
||||
value === "" ||
|
||||
value === undefined ||
|
||||
(value !== null &&
|
||||
typeof value === "object" &&
|
||||
!Object.keys(value).length)
|
||||
(typeof value === "object" && !Object.keys(value).length)
|
||||
) {
|
||||
delete data[key];
|
||||
delete this._stickySelector[key];
|
||||
@@ -988,12 +945,7 @@ export class HaServiceControl extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _dataChanged(
|
||||
ev: HASSDomEvent<{
|
||||
value: NonNullable<HaServiceControl["value"]>["data"];
|
||||
isValid?: boolean;
|
||||
}>
|
||||
) {
|
||||
private _dataChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
if (!ev.detail.isValid) {
|
||||
return;
|
||||
@@ -1017,15 +969,14 @@ export class HaServiceControl extends LitElement {
|
||||
|
||||
static styles = css`
|
||||
ha-settings-row {
|
||||
padding: var(--service-control-padding, 0 var(--ha-space-4));
|
||||
padding: var(--service-control-padding, 0 16px);
|
||||
}
|
||||
ha-settings-row[narrow] {
|
||||
padding-bottom: var(--ha-space-2);
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
ha-settings-row {
|
||||
--settings-row-content-width: 100%;
|
||||
--settings-row-prefix-display: contents;
|
||||
--ha-entities-picker-entity-min-width: 0;
|
||||
border-top: var(
|
||||
--service-control-items-border-top,
|
||||
1px solid var(--divider-color)
|
||||
@@ -1035,20 +986,20 @@ export class HaServiceControl extends LitElement {
|
||||
ha-entity-picker,
|
||||
ha-yaml-editor {
|
||||
display: block;
|
||||
margin: var(--service-control-padding, 0 var(--ha-space-4));
|
||||
margin: var(--service-control-padding, 0 16px);
|
||||
}
|
||||
ha-yaml-editor {
|
||||
padding: var(--ha-space-4) 0;
|
||||
padding: 16px 0;
|
||||
}
|
||||
p {
|
||||
margin: var(--service-control-padding, 0 var(--ha-space-4));
|
||||
padding: var(--ha-space-4) 0;
|
||||
margin: var(--service-control-padding, 0 16px);
|
||||
padding: 16px 0;
|
||||
}
|
||||
:host([hide-picker]) p {
|
||||
padding-top: 0;
|
||||
}
|
||||
.checkbox-spacer {
|
||||
width: var(--ha-space-8);
|
||||
width: 32px;
|
||||
}
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
@@ -1069,7 +1020,7 @@ export class HaServiceControl extends LitElement {
|
||||
}
|
||||
ha-expansion-panel {
|
||||
--ha-card-border-radius: var(--ha-border-radius-square);
|
||||
--expansion-panel-summary-padding: 0 var(--ha-space-4);
|
||||
--expansion-panel-summary-padding: 0 16px;
|
||||
--expansion-panel-content-padding: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
|
||||
const PASSIVE_EVENT_OPTIONS = { passive: true } as const;
|
||||
@@ -8,10 +8,6 @@ const PASSIVE_EVENT_OPTIONS = { passive: true } as const;
|
||||
export const haTopAppBarFixedStyles = css`
|
||||
:host {
|
||||
display: block;
|
||||
--total-top-app-bar-height: calc(
|
||||
var(--header-height, 0px) + var(--sub-row-height, 0px)
|
||||
);
|
||||
--sub-row-height: 0px;
|
||||
}
|
||||
|
||||
.top-app-bar {
|
||||
@@ -41,37 +37,14 @@ export const haTopAppBarFixedStyles = css`
|
||||
}
|
||||
|
||||
.row {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: var(--header-height);
|
||||
border-bottom: var(--app-header-border-bottom);
|
||||
}
|
||||
|
||||
.top-app-bar.has-sub-row .row {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.sub-row {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
border-bottom: var(--app-header-border-bottom);
|
||||
}
|
||||
|
||||
.sub-row slot,
|
||||
.sub-row ::slotted(*) {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sub-row[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -113,14 +86,8 @@ export const haTopAppBarFixedStyles = css`
|
||||
}
|
||||
|
||||
.top-app-bar-fixed-adjust {
|
||||
height: calc(
|
||||
100vh - var(--total-top-app-bar-height, 0px) - var(
|
||||
--safe-area-inset-top,
|
||||
0px
|
||||
) - var(--safe-area-inset-bottom, 0px)
|
||||
);
|
||||
padding-top: calc(
|
||||
var(--total-top-app-bar-height, 0px) + var(--safe-area-inset-top, 0px)
|
||||
var(--header-height, 0px) + var(--safe-area-inset-top, 0px)
|
||||
);
|
||||
padding-bottom: var(--safe-area-inset-bottom);
|
||||
padding-right: var(--safe-area-inset-right);
|
||||
@@ -139,14 +106,8 @@ export class HaTopAppBarFixed extends LitElement {
|
||||
|
||||
@query(".top-app-bar") protected _barElement!: HTMLElement;
|
||||
|
||||
@query(".sub-row") protected _subRowElement?: HTMLElement;
|
||||
|
||||
@state() private _hasSubRow = false;
|
||||
|
||||
private _scrollTarget?: HTMLElement | Window;
|
||||
|
||||
private _subRowResizeObserver?: ResizeObserver;
|
||||
|
||||
@property({ attribute: false })
|
||||
public get scrollTarget(): HTMLElement | Window {
|
||||
return this._scrollTarget || window;
|
||||
@@ -176,8 +137,6 @@ export class HaTopAppBarFixed extends LitElement {
|
||||
super.connectedCallback();
|
||||
|
||||
if (this.hasUpdated) {
|
||||
this._observeSubRowHeight();
|
||||
this._updateSubRowHeight();
|
||||
this._updateBarPosition();
|
||||
this._registerListeners();
|
||||
this._syncScrollState();
|
||||
@@ -194,7 +153,6 @@ export class HaTopAppBarFixed extends LitElement {
|
||||
<header
|
||||
class="top-app-bar ${classMap({
|
||||
"pane-header": paneHeader,
|
||||
"has-sub-row": this._hasSubRow,
|
||||
})}"
|
||||
>
|
||||
<div class="row">
|
||||
@@ -218,9 +176,6 @@ export class HaTopAppBarFixed extends LitElement {
|
||||
<slot name="actionItems"></slot>
|
||||
</section>
|
||||
</div>
|
||||
<div class="sub-row" ?hidden=${!this._hasSubRow}>
|
||||
<slot name="subRow" @slotchange=${this._subRowSlotChanged}></slot>
|
||||
</div>
|
||||
</header>
|
||||
`;
|
||||
}
|
||||
@@ -233,23 +188,13 @@ export class HaTopAppBarFixed extends LitElement {
|
||||
|
||||
protected firstUpdated(changedProperties: PropertyValues<this>) {
|
||||
super.firstUpdated(changedProperties);
|
||||
this._observeSubRowHeight();
|
||||
this._updateSubRowHeight();
|
||||
this._updateBarPosition();
|
||||
this._registerListeners();
|
||||
this._syncScrollState();
|
||||
}
|
||||
|
||||
protected override updated(changedProperties: PropertyValues) {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has("_hasSubRow")) {
|
||||
this._updateSubRowHeight();
|
||||
}
|
||||
}
|
||||
|
||||
override disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._unobserveSubRowHeight();
|
||||
this._unregisterListeners();
|
||||
}
|
||||
|
||||
@@ -280,40 +225,6 @@ export class HaTopAppBarFixed extends LitElement {
|
||||
this.scrollTarget.removeEventListener("scroll", this._syncScrollState);
|
||||
}
|
||||
|
||||
private _observeSubRowHeight() {
|
||||
if (
|
||||
this._subRowResizeObserver ||
|
||||
!this._subRowElement ||
|
||||
typeof ResizeObserver === "undefined"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this._subRowResizeObserver = new ResizeObserver(this._updateSubRowHeight);
|
||||
this._subRowResizeObserver.observe(this._subRowElement);
|
||||
}
|
||||
|
||||
private _unobserveSubRowHeight() {
|
||||
this._subRowResizeObserver?.disconnect();
|
||||
this._subRowResizeObserver = undefined;
|
||||
}
|
||||
|
||||
private _subRowSlotChanged = (ev: Event) => {
|
||||
const slot = ev.currentTarget as HTMLSlotElement;
|
||||
this._hasSubRow = slot
|
||||
.assignedNodes({ flatten: true })
|
||||
.some(
|
||||
(node) =>
|
||||
node.nodeType !== Node.TEXT_NODE || Boolean(node.textContent?.trim())
|
||||
);
|
||||
};
|
||||
|
||||
private _updateSubRowHeight = () => {
|
||||
const subRowHeight = this._hasSubRow
|
||||
? this._subRowElement?.offsetHeight || 0
|
||||
: 0;
|
||||
this.style.setProperty("--sub-row-height", `${subRowHeight}px`);
|
||||
};
|
||||
|
||||
static override styles: CSSResultGroup = haTopAppBarFixedStyles;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,9 +108,9 @@ export class HaTwoPaneTopAppBarFixed extends HaTopAppBarFixed {
|
||||
css`
|
||||
.shadow-container {
|
||||
position: absolute;
|
||||
top: calc(-1 * var(--total-top-app-bar-height));
|
||||
top: calc(-1 * var(--header-height));
|
||||
width: 100%;
|
||||
height: var(--total-top-app-bar-height);
|
||||
height: var(--header-height);
|
||||
z-index: 1;
|
||||
transition: box-shadow 200ms linear;
|
||||
}
|
||||
@@ -131,7 +131,7 @@ export class HaTwoPaneTopAppBarFixed extends HaTopAppBarFixed {
|
||||
.top-app-bar-fixed-adjust--pane {
|
||||
display: flex;
|
||||
height: calc(
|
||||
100vh - var(--total-top-app-bar-height, 0px) - var(
|
||||
100vh - var(--header-height, 0px) - var(
|
||||
--safe-area-inset-top,
|
||||
0px
|
||||
) - var(--safe-area-inset-bottom, 0px)
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { mdiStop, mdiValveClosed, mdiValveOpen } from "@mdi/js";
|
||||
import { LitElement, html, css, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { consumeLocalize } from "../common/decorators/consume-context-entry";
|
||||
import { supportsFeature } from "../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import { apiContext } from "../data/context";
|
||||
import type { ValveEntity } from "../data/valve";
|
||||
import { ValveEntityFeature, canClose, canOpen, canStop } from "../data/valve";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-icon-button";
|
||||
|
||||
@customElement("ha-valve-controls")
|
||||
class HaValveControls extends LitElement {
|
||||
@state() @consumeLocalize() private _localize!: LocalizeFunc;
|
||||
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: ContextType<typeof apiContext>;
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj!: ValveEntity;
|
||||
|
||||
@@ -31,7 +25,7 @@ class HaValveControls extends LitElement {
|
||||
class=${classMap({
|
||||
hidden: !supportsFeature(this.stateObj, ValveEntityFeature.OPEN),
|
||||
})}
|
||||
.label=${this._localize("ui.card.valve.open_valve")}
|
||||
.label=${this.hass.localize("ui.card.valve.open_valve")}
|
||||
@click=${this._onOpenTap}
|
||||
.disabled=${!canOpen(this.stateObj)}
|
||||
.path=${mdiValveOpen}
|
||||
@@ -41,7 +35,7 @@ class HaValveControls extends LitElement {
|
||||
class=${classMap({
|
||||
hidden: !supportsFeature(this.stateObj, ValveEntityFeature.STOP),
|
||||
})}
|
||||
.label=${this._localize("ui.card.valve.stop_valve")}
|
||||
.label=${this.hass.localize("ui.card.valve.stop_valve")}
|
||||
@click=${this._onStopTap}
|
||||
.disabled=${!canStop(this.stateObj)}
|
||||
.path=${mdiStop}
|
||||
@@ -50,7 +44,7 @@ class HaValveControls extends LitElement {
|
||||
class=${classMap({
|
||||
hidden: !supportsFeature(this.stateObj, ValveEntityFeature.CLOSE),
|
||||
})}
|
||||
.label=${this._localize("ui.card.valve.close_valve")}
|
||||
.label=${this.hass.localize("ui.card.valve.close_valve")}
|
||||
@click=${this._onCloseTap}
|
||||
.disabled=${!canClose(this.stateObj)}
|
||||
.path=${mdiValveClosed}
|
||||
@@ -62,21 +56,21 @@ class HaValveControls extends LitElement {
|
||||
|
||||
private _onOpenTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this._api.callService("valve", "open_valve", {
|
||||
this.hass.callService("valve", "open_valve", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _onCloseTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this._api.callService("valve", "close_valve", {
|
||||
this.hass.callService("valve", "close_valve", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _onStopTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this._api.callService("valve", "stop_valve", {
|
||||
this.hass.callService("valve", "stop_valve", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import type { LeafletModuleType } from "../../common/dom/setup-leaflet-map";
|
||||
import { setupLeafletMap } from "../../common/dom/setup-leaflet-map";
|
||||
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
||||
import { computeStateName } from "../../common/entity/compute_state_name";
|
||||
import { getEntityLocation } from "../../common/entity/get_entity_location";
|
||||
import { DecoratedMarker } from "../../common/map/decorated_marker";
|
||||
import { filterXSS } from "../../common/util/xss";
|
||||
import type { HomeAssistant, ThemeMode } from "../../types";
|
||||
@@ -585,17 +584,18 @@ export class HaMap extends ReactiveElement {
|
||||
const customTitle = typeof entity !== "string" ? entity.name : undefined;
|
||||
const title = customTitle ?? computeStateName(stateObj);
|
||||
const {
|
||||
latitude,
|
||||
longitude,
|
||||
passive,
|
||||
icon,
|
||||
radius,
|
||||
entity_picture: entityPicture,
|
||||
gps_accuracy: gpsAccuracy,
|
||||
} = stateObj.attributes;
|
||||
|
||||
const location = getEntityLocation(stateObj, hass.states);
|
||||
if (!location) {
|
||||
if (!(latitude && longitude)) {
|
||||
continue;
|
||||
}
|
||||
const { latitude, longitude, gpsAccuracy } = location;
|
||||
|
||||
if (computeStateDomain(stateObj) === "zone") {
|
||||
// DRAW ZONE
|
||||
|
||||
@@ -26,13 +26,6 @@ export interface HomeFrontendSystemData {
|
||||
shortcuts?: ShortcutItem[];
|
||||
}
|
||||
|
||||
export interface EnergyFrontendSystemData {
|
||||
// Stable "<view>.<card-type>" keys of energy dashboard cards the user has
|
||||
// hidden. An absent key or array means nothing is hidden (all cards visible),
|
||||
// so cards added in the future are shown by default.
|
||||
hidden_cards?: string[];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface FrontendUserData {
|
||||
core: CoreFrontendUserData;
|
||||
@@ -41,7 +34,6 @@ declare global {
|
||||
interface FrontendSystemData {
|
||||
core: CoreFrontendSystemData;
|
||||
home: HomeFrontendSystemData;
|
||||
energy: EnergyFrontendSystemData;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +123,8 @@ class EntityPreviewRow extends LitElement {
|
||||
const climateDomains = ["climate", "water_heater"];
|
||||
if (climateDomains.includes(domain)) {
|
||||
return html`
|
||||
<ha-climate-state .stateObj=${stateObj}> </ha-climate-state>
|
||||
<ha-climate-state .hass=${this.hass} .stateObj=${stateObj}>
|
||||
</ha-climate-state>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -132,11 +133,15 @@ class EntityPreviewRow extends LitElement {
|
||||
${isTiltOnly(stateObj)
|
||||
? html`
|
||||
<ha-cover-tilt-controls
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
></ha-cover-tilt-controls>
|
||||
`
|
||||
: html`
|
||||
<ha-cover-controls .stateObj=${stateObj}></ha-cover-controls>
|
||||
<ha-cover-controls
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
></ha-cover-controls>
|
||||
`}
|
||||
`;
|
||||
}
|
||||
@@ -211,7 +216,8 @@ class EntityPreviewRow extends LitElement {
|
||||
|
||||
if (domain === "humidifier") {
|
||||
return html`
|
||||
<ha-humidifier-state .stateObj=${stateObj}> </ha-humidifier-state>
|
||||
<ha-humidifier-state .hass=${this.hass} .stateObj=${stateObj}>
|
||||
</ha-humidifier-state>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { getEntityLocation } from "../../../common/entity/get_entity_location";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/map/ha-map";
|
||||
import { showZoneEditor } from "../../../data/zone";
|
||||
@@ -22,13 +21,8 @@ class MoreInfoPerson extends LitElement {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const location = getEntityLocation(this.stateObj, this.hass.states);
|
||||
const hasOwnCoordinates =
|
||||
typeof this.stateObj.attributes.latitude === "number" &&
|
||||
typeof this.stateObj.attributes.longitude === "number";
|
||||
|
||||
return html`
|
||||
${location
|
||||
${this.stateObj.attributes.latitude && this.stateObj.attributes.longitude
|
||||
? html`
|
||||
<ha-map
|
||||
.hass=${this.hass}
|
||||
@@ -37,7 +31,10 @@ class MoreInfoPerson extends LitElement {
|
||||
></ha-map>
|
||||
`
|
||||
: ""}
|
||||
${!__DEMO__ && this.hass.user?.is_admin && hasOwnCoordinates
|
||||
${!__DEMO__ &&
|
||||
this.hass.user?.is_admin &&
|
||||
this.stateObj.attributes.latitude &&
|
||||
this.stateObj.attributes.longitude
|
||||
? html`
|
||||
<div class="actions">
|
||||
<ha-button
|
||||
|
||||
@@ -190,6 +190,7 @@ class HaMoreInfoDetails extends LitElement {
|
||||
</div>
|
||||
<div class="value">
|
||||
<ha-attribute-value
|
||||
.hass=${this.hass}
|
||||
.attribute=${attribute}
|
||||
.stateObj=${this._stateObj}
|
||||
></ha-attribute-value>
|
||||
|
||||
@@ -132,6 +132,7 @@ export class HuiNotificationDrawer extends KeyboardShortcutMixin(LitElement) {
|
||||
</div>
|
||||
<ha-icon-button-prev
|
||||
slot="actionItems"
|
||||
.hass=${this.hass}
|
||||
@click=${this.closeDialog}
|
||||
.label=${this.hass.localize("ui.notification_drawer.close")}
|
||||
>
|
||||
|
||||
@@ -25,7 +25,12 @@ class HassErrorScreen extends LitElement {
|
||||
${this.toolbar
|
||||
? html`<div class="toolbar">
|
||||
${this.rootnav || history.state?.root
|
||||
? html`<ha-menu-button></ha-menu-button>`
|
||||
? html`
|
||||
<ha-menu-button
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
`
|
||||
: html`
|
||||
<ha-icon-button-arrow-prev
|
||||
@click=${this._handleBack}
|
||||
|
||||
@@ -27,7 +27,12 @@ class HassLoadingScreen extends LitElement {
|
||||
? ""
|
||||
: html`<div class="toolbar">
|
||||
${this.rootnav || history.state?.root
|
||||
? html`<ha-menu-button></ha-menu-button>`
|
||||
? html`
|
||||
<ha-menu-button
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
`
|
||||
: html`
|
||||
<ha-icon-button-arrow-prev
|
||||
@click=${this._handleBack}
|
||||
|
||||
@@ -33,7 +33,12 @@ class HassSubpage extends LitElement {
|
||||
<div class="toolbar ${classMap({ narrow: this.narrow })}">
|
||||
<div class="toolbar-content">
|
||||
${this.mainPage || history.state?.root
|
||||
? html`<ha-menu-button></ha-menu-button>`
|
||||
? html`
|
||||
<ha-menu-button
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
`
|
||||
: this.backPath
|
||||
? html`
|
||||
<ha-icon-button-arrow-prev
|
||||
|
||||
@@ -165,7 +165,12 @@ export class HassTabsSubpage extends LitElement {
|
||||
<slot name="toolbar">
|
||||
<div class="toolbar-content">
|
||||
${this.mainPage || (!this.backPath && history.state?.root)
|
||||
? html`<ha-menu-button></ha-menu-button>`
|
||||
? html`
|
||||
<ha-menu-button
|
||||
.hass=${this.hass}
|
||||
.narrow=${this._narrow}
|
||||
></ha-menu-button>
|
||||
`
|
||||
: this.backPath
|
||||
? html`
|
||||
<ha-icon-button-arrow-prev
|
||||
|
||||
@@ -119,7 +119,11 @@ class PanelCalendar extends SubscribeMixin(LitElement) {
|
||||
if (!this._entityRegistry) {
|
||||
return html`
|
||||
<ha-two-pane-top-app-bar-fixed .narrow=${this.narrow}>
|
||||
<ha-menu-button slot="navigationIcon"></ha-menu-button>
|
||||
<ha-menu-button
|
||||
slot="navigationIcon"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
<div slot="title">
|
||||
${this.hass.localize("ui.components.calendar.my_calendars")}
|
||||
</div>
|
||||
@@ -154,7 +158,11 @@ class PanelCalendar extends SubscribeMixin(LitElement) {
|
||||
footer
|
||||
.narrow=${this.narrow}
|
||||
>
|
||||
<ha-menu-button slot="navigationIcon"></ha-menu-button>
|
||||
<ha-menu-button
|
||||
slot="navigationIcon"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
|
||||
${!showPane
|
||||
? html`<ha-dropdown slot="title">
|
||||
|
||||
@@ -105,7 +105,13 @@ class PanelClimate extends LitElement {
|
||||
slot="navigationIcon"
|
||||
></ha-icon-button-arrow-prev>
|
||||
`
|
||||
: html`<ha-menu-button slot="navigationIcon"></ha-menu-button>`}
|
||||
: html`
|
||||
<ha-menu-button
|
||||
slot="navigationIcon"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
`}
|
||||
<div slot="title">${this.hass.localize("panel.climate")}</div>
|
||||
${this._lovelace
|
||||
? html`
|
||||
|
||||
@@ -45,7 +45,6 @@ class SupervisorAppsState extends LitElement {
|
||||
}
|
||||
.dot.state-started {
|
||||
background-color: var(--ha-color-green-80);
|
||||
animation: state-dot-pulse 1.8s infinite;
|
||||
}
|
||||
.dot.state-startup {
|
||||
background-color: var(--ha-color-on-warning-normal);
|
||||
|
||||
@@ -206,6 +206,7 @@ class DialogAreaDetail
|
||||
)}
|
||||
</p>
|
||||
<ha-aliases-editor
|
||||
.hass=${this.hass}
|
||||
.aliases=${this._aliases}
|
||||
@value-changed=${this._aliasesChanged}
|
||||
></ha-aliases-editor>
|
||||
|
||||
@@ -229,6 +229,7 @@ class DialogFloorDetail extends LitElement {
|
||||
)}
|
||||
</p>
|
||||
<ha-aliases-editor
|
||||
.hass=${this.hass}
|
||||
.aliases=${this._aliases}
|
||||
@value-changed=${this._aliasesChanged}
|
||||
></ha-aliases-editor>
|
||||
|
||||
@@ -232,6 +232,7 @@ class HaConfigSectionUpdates extends LitElement {
|
||||
: nothing}
|
||||
</div>
|
||||
<ha-config-updates
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.updateEntities=${group.entities}
|
||||
showAll
|
||||
@@ -255,6 +256,7 @@ class HaConfigSectionUpdates extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
<ha-config-updates
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.updateEntities=${skippedUpdates}
|
||||
showAll
|
||||
@@ -278,6 +280,7 @@ class HaConfigSectionUpdates extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
<ha-config-updates
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.updateEntities=${notInstallableUpdates}
|
||||
showAll
|
||||
|
||||
@@ -236,7 +236,11 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
|
||||
return html`
|
||||
<ha-top-app-bar-fixed .narrow=${this.narrow}>
|
||||
<ha-menu-button slot="navigationIcon"></ha-menu-button>
|
||||
<ha-menu-button
|
||||
slot="navigationIcon"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
<div slot="title">${this.hass.localize("panel.config")}</div>
|
||||
|
||||
<ha-icon-button
|
||||
@@ -322,6 +326,7 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
</a>
|
||||
</div>
|
||||
<ha-config-updates
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.updateEntities=${canInstallUpdates}
|
||||
></ha-config-updates>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { consumeLocalize } from "../../../common/decorators/consume-context-entry";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { computeDeviceNameDisplay } from "../../../common/entity/compute_device_name";
|
||||
import { getDeviceArea } from "../../../common/entity/context/get_device_context";
|
||||
@@ -14,51 +14,62 @@ import "../../../components/ha-spinner";
|
||||
import "../../../components/item/ha-list-item-button";
|
||||
import "../../../components/list/ha-list-base";
|
||||
import "../../../components/progress/ha-progress-ring";
|
||||
import {
|
||||
areasContext,
|
||||
devicesContext,
|
||||
fullEntitiesContext,
|
||||
statesContext,
|
||||
} from "../../../data/context";
|
||||
import { entityRegistryByEntityId } from "../../../data/entity/entity_registry";
|
||||
import type { DeviceRegistryEntry } from "../../../data/device/device_registry";
|
||||
import { subscribeDeviceRegistry } from "../../../data/device/device_registry";
|
||||
import type { EntityRegistryEntry } from "../../../data/entity/entity_registry";
|
||||
import { subscribeEntityRegistry } from "../../../data/entity/entity_registry";
|
||||
import type { UpdateEntity } from "../../../data/update";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
|
||||
@customElement("ha-config-updates")
|
||||
class HaConfigUpdates extends LitElement {
|
||||
class HaConfigUpdates extends SubscribeMixin(LitElement) {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ attribute: false }) public updateEntities?: UpdateEntity[];
|
||||
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
@state() private _devices?: DeviceRegistryEntry[];
|
||||
|
||||
@consume({ context: statesContext, subscribe: true })
|
||||
private _states!: ContextType<typeof statesContext>;
|
||||
@state() private _entities?: EntityRegistryEntry[];
|
||||
|
||||
@consume({ context: devicesContext, subscribe: true })
|
||||
private _devices!: ContextType<typeof devicesContext>;
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
subscribeDeviceRegistry(this.hass.connection, (entries) => {
|
||||
this._devices = entries;
|
||||
}),
|
||||
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
||||
this._entities = entities.filter((entity) => entity.device_id !== null);
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
@consume({ context: areasContext, subscribe: true })
|
||||
private _areas!: ContextType<typeof areasContext>;
|
||||
private getDeviceEntry = memoizeOne(
|
||||
(deviceId: string): DeviceRegistryEntry | undefined =>
|
||||
this._devices?.find((device) => device.id === deviceId)
|
||||
);
|
||||
|
||||
@state()
|
||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||
private _entities: ContextType<typeof fullEntitiesContext> = [];
|
||||
private getEntityEntry = memoizeOne(
|
||||
(entityId: string): EntityRegistryEntry | undefined =>
|
||||
this._entities?.find((entity) => entity.entity_id === entityId)
|
||||
);
|
||||
|
||||
private _renderUpdateProgress(entity: UpdateEntity) {
|
||||
if (entity.attributes.update_percentage != null) {
|
||||
return html`<ha-progress-ring
|
||||
size="small"
|
||||
.value=${entity.attributes.update_percentage}
|
||||
.label=${this._localize("ui.panel.config.updates.update_in_progress")}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.updates.update_in_progress"
|
||||
)}
|
||||
></ha-progress-ring>`;
|
||||
}
|
||||
|
||||
if (entity.attributes.in_progress) {
|
||||
return html`<ha-spinner
|
||||
size="small"
|
||||
.ariaLabel=${this._localize(
|
||||
.ariaLabel=${this.hass.localize(
|
||||
"ui.panel.config.updates.update_in_progress"
|
||||
)}
|
||||
></ha-spinner>`;
|
||||
@@ -73,23 +84,22 @@ class HaConfigUpdates extends LitElement {
|
||||
}
|
||||
|
||||
const updates = this.updateEntities;
|
||||
const entities = entityRegistryByEntityId(this._entities);
|
||||
|
||||
return html`
|
||||
<ha-list-base
|
||||
aria-label=${this._localize("ui.panel.config.updates.caption")}
|
||||
aria-label=${this.hass.localize("ui.panel.config.updates.caption")}
|
||||
>
|
||||
${updates.map((entity) => {
|
||||
const entityEntry = entities[entity.entity_id];
|
||||
const entityEntry = this.getEntityEntry(entity.entity_id);
|
||||
const deviceEntry =
|
||||
entityEntry && entityEntry.device_id
|
||||
? this._devices[entityEntry.device_id]
|
||||
? this.getDeviceEntry(entityEntry.device_id)
|
||||
: undefined;
|
||||
|
||||
const areaName =
|
||||
deviceEntry && deviceEntry.entry_type !== "service"
|
||||
? getDeviceArea(deviceEntry, this._areas)?.name ||
|
||||
this._localize("ui.panel.config.updates.no_area")
|
||||
? getDeviceArea(deviceEntry, this.hass.areas)?.name ||
|
||||
this.hass.localize("ui.panel.config.updates.no_area")
|
||||
: undefined;
|
||||
|
||||
return html`
|
||||
@@ -102,6 +112,7 @@ class HaConfigUpdates extends LitElement {
|
||||
<state-badge
|
||||
.title=${entity.attributes.title ||
|
||||
entity.attributes.friendly_name}
|
||||
.hass=${this.hass}
|
||||
.stateObj=${entity}
|
||||
class=${ifDefined(
|
||||
this.narrow && entity.attributes.in_progress
|
||||
@@ -119,8 +130,8 @@ class HaConfigUpdates extends LitElement {
|
||||
>${deviceEntry
|
||||
? computeDeviceNameDisplay(
|
||||
deviceEntry,
|
||||
this._localize,
|
||||
this._states
|
||||
this.hass.localize,
|
||||
this.hass.states
|
||||
)
|
||||
: entity.attributes.friendly_name}</span
|
||||
>
|
||||
@@ -128,7 +139,7 @@ class HaConfigUpdates extends LitElement {
|
||||
${areaName ? html`${areaName} ⸱ ` : nothing}
|
||||
${entity.attributes.title} ${entity.attributes.latest_version}
|
||||
${entity.attributes.skipped_version
|
||||
? `(${this._localize("ui.panel.config.updates.skipped")})`
|
||||
? `(${this.hass.localize("ui.panel.config.updates.skipped")})`
|
||||
: nothing}
|
||||
</span>
|
||||
${!this.narrow
|
||||
|
||||
@@ -2,49 +2,20 @@ import { mdiDotsVertical } from "@mdi/js";
|
||||
import type { CSSResultGroup, TemplateResult, PropertyValues } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import "../../../components/ha-dropdown";
|
||||
import "../../../components/ha-dropdown-item";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-icon-button-arrow-prev";
|
||||
import "../../../components/ha-menu-button";
|
||||
import "../../../components/ha-tab-group";
|
||||
import "../../../components/ha-tab-group-tab";
|
||||
import "../../../components/ha-top-app-bar-fixed";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../../types";
|
||||
import "./developer-tools-router";
|
||||
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
|
||||
|
||||
const DEVELOPER_TOOLS_TABS = [
|
||||
{
|
||||
panel: "yaml",
|
||||
translationKey: "ui.panel.config.developer-tools.tabs.yaml.title",
|
||||
},
|
||||
{
|
||||
panel: "state",
|
||||
translationKey: "ui.panel.config.developer-tools.tabs.states.title",
|
||||
},
|
||||
{
|
||||
panel: "action",
|
||||
translationKey: "ui.panel.config.developer-tools.tabs.actions.title",
|
||||
},
|
||||
{
|
||||
panel: "template",
|
||||
translationKey: "ui.panel.config.developer-tools.tabs.templates.title",
|
||||
},
|
||||
{
|
||||
panel: "event",
|
||||
translationKey: "ui.panel.config.developer-tools.tabs.events.title",
|
||||
},
|
||||
{
|
||||
panel: "statistics",
|
||||
translationKey: "ui.panel.config.developer-tools.tabs.statistics.title",
|
||||
},
|
||||
{
|
||||
panel: "assist",
|
||||
translationKey: "ui.panel.config.developer-tools.tabs.assist.tab",
|
||||
},
|
||||
] as const;
|
||||
|
||||
@customElement("ha-panel-developer-tools")
|
||||
class PanelDeveloperTools extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@@ -61,47 +32,94 @@ class PanelDeveloperTools extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
const page = this._page;
|
||||
return html`
|
||||
<ha-top-app-bar-fixed .narrow=${this.narrow}>
|
||||
<ha-icon-button-arrow-prev
|
||||
slot="navigationIcon"
|
||||
@click=${this._handleBack}
|
||||
></ha-icon-button-arrow-prev>
|
||||
<div slot="title">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.dashboard.developer_tools.main"
|
||||
)}
|
||||
</div>
|
||||
<ha-dropdown slot="actionItems" @wa-select=${this._handleMenuAction}>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.hass.localize("ui.common.menu")}
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
<ha-dropdown-item value="debug">
|
||||
<div class="header ${classMap({ narrow: this.narrow })}">
|
||||
<div class="toolbar">
|
||||
<ha-icon-button-arrow-prev
|
||||
slot="navigationIcon"
|
||||
@click=${this._handleBack}
|
||||
></ha-icon-button-arrow-prev>
|
||||
<div class="main-title">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.developer-tools.tabs.debug.title"
|
||||
"ui.panel.config.dashboard.developer_tools.main"
|
||||
)}
|
||||
</ha-dropdown-item>
|
||||
</ha-dropdown>
|
||||
<ha-tab-group @wa-tab-show=${this._handlePageSelected} slot="subRow">
|
||||
${DEVELOPER_TOOLS_TABS.map(
|
||||
(tab) => html`
|
||||
<ha-tab-group-tab
|
||||
slot="nav"
|
||||
panel=${tab.panel}
|
||||
.active=${page === tab.panel}
|
||||
>
|
||||
${this.hass.localize(tab.translationKey)}
|
||||
</ha-tab-group-tab>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
<ha-dropdown slot="actionItems" @wa-select=${this._handleMenuAction}>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.hass.localize("ui.common.menu")}
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
<ha-dropdown-item value="debug">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.developer-tools.tabs.debug.title"
|
||||
)}
|
||||
</ha-dropdown-item>
|
||||
</ha-dropdown>
|
||||
</div>
|
||||
<ha-tab-group @wa-tab-show=${this._handlePageSelected}>
|
||||
<ha-tab-group-tab slot="nav" panel="yaml" .active=${page === "yaml"}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.developer-tools.tabs.yaml.title"
|
||||
)}
|
||||
</ha-tab-group-tab>
|
||||
<ha-tab-group-tab
|
||||
slot="nav"
|
||||
panel="state"
|
||||
.active=${page === "state"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.developer-tools.tabs.states.title"
|
||||
)}
|
||||
</ha-tab-group-tab>
|
||||
<ha-tab-group-tab
|
||||
slot="nav"
|
||||
panel="action"
|
||||
.active=${page === "action"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.developer-tools.tabs.actions.title"
|
||||
)}
|
||||
</ha-tab-group-tab>
|
||||
<ha-tab-group-tab
|
||||
slot="nav"
|
||||
panel="template"
|
||||
.active=${page === "template"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.developer-tools.tabs.templates.title"
|
||||
)}
|
||||
</ha-tab-group-tab>
|
||||
<ha-tab-group-tab
|
||||
slot="nav"
|
||||
panel="event"
|
||||
.active=${page === "event"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.title"
|
||||
)}
|
||||
</ha-tab-group-tab>
|
||||
<ha-tab-group-tab
|
||||
slot="nav"
|
||||
panel="statistics"
|
||||
.active=${page === "statistics"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.developer-tools.tabs.statistics.title"
|
||||
)}
|
||||
</ha-tab-group-tab>
|
||||
<ha-tab-group-tab
|
||||
slot="nav"
|
||||
panel="assist"
|
||||
.active=${page === "assist"}
|
||||
>Assist</ha-tab-group-tab
|
||||
>
|
||||
</ha-tab-group>
|
||||
<developer-tools-router
|
||||
.route=${this.route}
|
||||
.narrow=${this.narrow}
|
||||
.hass=${this.hass}
|
||||
></developer-tools-router>
|
||||
</ha-top-app-bar-fixed>
|
||||
</div>
|
||||
<developer-tools-router
|
||||
.route=${this.route}
|
||||
.narrow=${this.narrow}
|
||||
.hass=${this.hass}
|
||||
></developer-tools-router>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -132,17 +150,90 @@ class PanelDeveloperTools extends LitElement {
|
||||
navigate("/config");
|
||||
}
|
||||
|
||||
static readonly styles: CSSResultGroup = css`
|
||||
developer-tools-router {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
ha-tab-group {
|
||||
--ha-tab-active-text-color: var(--app-header-text-color, white);
|
||||
--ha-tab-indicator-color: var(--app-header-text-color, white);
|
||||
--ha-tab-track-color: transparent;
|
||||
}
|
||||
`;
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
color: var(--primary-text-color);
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 4;
|
||||
background-color: var(--app-header-background-color);
|
||||
width: calc(
|
||||
var(--ha-top-app-bar-width, 100%) - var(
|
||||
--safe-area-inset-right,
|
||||
0px
|
||||
)
|
||||
);
|
||||
padding-top: var(--safe-area-inset-top);
|
||||
padding-right: var(--safe-area-inset-right);
|
||||
color: var(--app-header-text-color, white);
|
||||
-webkit-backdrop-filter: var(--app-header-backdrop-filter, none);
|
||||
backdrop-filter: var(--app-header-backdrop-filter, none);
|
||||
}
|
||||
:host([narrow]) .header {
|
||||
width: calc(
|
||||
var(--ha-top-app-bar-width, 100%) - var(
|
||||
--safe-area-inset-left,
|
||||
0px
|
||||
) - var(--safe-area-inset-right, 0px)
|
||||
);
|
||||
padding-left: var(--safe-area-inset-left);
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
height: var(--header-height);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: var(--ha-font-size-xl);
|
||||
padding: var(--ha-space-2) var(--ha-space-3);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
:host([narrow]) .toolbar {
|
||||
padding: var(--ha-space-1);
|
||||
}
|
||||
.main-title {
|
||||
margin-inline-start: var(--ha-space-6);
|
||||
line-height: var(--ha-line-height-normal);
|
||||
flex-grow: 1;
|
||||
}
|
||||
.narrow .main-title {
|
||||
margin-inline-start: var(--ha-space-2);
|
||||
}
|
||||
developer-tools-router {
|
||||
display: block;
|
||||
padding-top: calc(
|
||||
var(--header-height) + 52px + var(--safe-area-inset-top, 0px)
|
||||
);
|
||||
padding-bottom: var(--safe-area-inset-bottom);
|
||||
padding-right: var(--safe-area-inset-right);
|
||||
flex: 1 1 100%;
|
||||
max-width: calc(100% - var(--safe-area-inset-right, 0px));
|
||||
}
|
||||
:host([narrow]) developer-tools-router {
|
||||
padding-left: var(--safe-area-inset-left);
|
||||
max-width: calc(
|
||||
100% - var(--safe-area-inset-left, 0px) - var(
|
||||
--safe-area-inset-right,
|
||||
0px
|
||||
)
|
||||
);
|
||||
}
|
||||
ha-tab-group {
|
||||
--ha-tab-active-text-color: var(--app-header-text-color, white);
|
||||
--ha-tab-indicator-color: var(--app-header-text-color, white);
|
||||
--ha-tab-track-color: transparent;
|
||||
border-bottom: var(--app-header-border-bottom, none);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -602,6 +602,11 @@ class HaPanelDevState extends LitElement {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:host {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
|
||||
@@ -846,6 +846,7 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
@expanded-changed=${this._filterExpanded}
|
||||
></ha-filter-floor-areas>
|
||||
<ha-filter-integrations
|
||||
.hass=${this.hass}
|
||||
.value=${this._filters["ha-filter-integrations"]?.value}
|
||||
@data-table-filter-changed=${this._filterChanged}
|
||||
slot="filter-pane"
|
||||
|
||||
@@ -1,288 +0,0 @@
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { LocalizeKeys } from "../../../../common/translations/localize";
|
||||
import "../../../../components/ha-alert";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-dialog";
|
||||
import "../../../../components/ha-dialog-footer";
|
||||
import "../../../../components/ha-expansion-panel";
|
||||
import "../../../../components/ha-settings-row";
|
||||
import "../../../../components/ha-spinner";
|
||||
import "../../../../components/ha-switch";
|
||||
import type { HaSwitch } from "../../../../components/ha-switch";
|
||||
import "../../../../components/ha-tooltip";
|
||||
import {
|
||||
connectionContext,
|
||||
internationalizationContext,
|
||||
} from "../../../../data/context";
|
||||
import {
|
||||
fetchFrontendSystemData,
|
||||
saveFrontendSystemData,
|
||||
} from "../../../../data/frontend";
|
||||
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
|
||||
import { haStyleDialog } from "../../../../resources/styles";
|
||||
import { showToast } from "../../../../util/toast";
|
||||
import type {
|
||||
EnergyCardCatalogEntry,
|
||||
EnergyViewPath,
|
||||
} from "../../../energy/strategies/energy-cards";
|
||||
import { ENERGY_CARD_CATALOG } from "../../../energy/strategies/energy-cards";
|
||||
import type { EnergyCustomiseDialogParams } from "./show-dialog-energy-customise";
|
||||
|
||||
const VIEW_GROUPS: { view: EnergyViewPath; labelKey: LocalizeKeys }[] = [
|
||||
{
|
||||
view: "overview",
|
||||
labelKey: "ui.panel.config.energy.customise.groups.overview",
|
||||
},
|
||||
{ view: "electricity", labelKey: "ui.panel.config.energy.tabs.electricity" },
|
||||
{ view: "gas", labelKey: "ui.panel.config.energy.tabs.gas" },
|
||||
{ view: "water", labelKey: "ui.panel.config.energy.tabs.water" },
|
||||
{ view: "now", labelKey: "ui.panel.config.energy.customise.groups.now" },
|
||||
];
|
||||
|
||||
@customElement("dialog-energy-customise")
|
||||
export class DialogEnergyCustomise
|
||||
extends LitElement
|
||||
implements HassDialog<EnergyCustomiseDialogParams>
|
||||
{
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: connectionContext, subscribe: true })
|
||||
private _connection!: ContextType<typeof connectionContext>;
|
||||
|
||||
@state() private _params?: EnergyCustomiseDialogParams;
|
||||
|
||||
@state() private _open = false;
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
@state() private _submitting = false;
|
||||
|
||||
// Working copy of the hidden card keys. A switch that is ON means the card is
|
||||
// visible, i.e. its key is NOT in this set.
|
||||
@state() private _hidden?: Set<string>;
|
||||
|
||||
public showDialog(params: EnergyCustomiseDialogParams): void {
|
||||
this._params = params;
|
||||
this._open = true;
|
||||
this._loadHidden();
|
||||
}
|
||||
|
||||
public closeDialog(): boolean {
|
||||
this._open = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private _dialogClosed(): void {
|
||||
this._params = undefined;
|
||||
this._hidden = undefined;
|
||||
this._error = undefined;
|
||||
this._submitting = false;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
private async _loadHidden(): Promise<void> {
|
||||
this._error = undefined;
|
||||
// showDialog runs before the dialog is connected to the DOM, so wait for
|
||||
// the first update to ensure the consumed contexts have resolved.
|
||||
await this.updateComplete;
|
||||
if (!this._params) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// The card labels reuse keys from the "energy" translation fragment,
|
||||
// which is not guaranteed to be loaded on the config page.
|
||||
const [data] = await Promise.all([
|
||||
fetchFrontendSystemData(this._connection.connection, "energy"),
|
||||
this._i18n.loadFragmentTranslation("energy"),
|
||||
]);
|
||||
this._hidden = new Set(data?.hidden_cards ?? []);
|
||||
} catch (err: any) {
|
||||
this._error = err?.message || "Unknown error";
|
||||
this._hidden = new Set();
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._params) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
.open=${this._open}
|
||||
.headerTitle=${this._i18n.localize(
|
||||
"ui.panel.config.energy.customise.title"
|
||||
)}
|
||||
prevent-scrim-close
|
||||
@closed=${this._dialogClosed}
|
||||
>
|
||||
${!this._hidden
|
||||
? html`<div class="loading">
|
||||
<ha-spinner size="large"></ha-spinner>
|
||||
</div>`
|
||||
: this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: html`<div class="groups">${this._renderGroups()}</div>`}
|
||||
|
||||
<ha-dialog-footer slot="footer">
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
slot="secondaryAction"
|
||||
@click=${this.closeDialog}
|
||||
.disabled=${this._submitting}
|
||||
>
|
||||
${this._i18n.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
slot="primaryAction"
|
||||
@click=${this._save}
|
||||
.disabled=${this._submitting || !this._hidden || !!this._error}
|
||||
>
|
||||
${this._i18n.localize("ui.common.save")}
|
||||
</ha-button>
|
||||
</ha-dialog-footer>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderGroups() {
|
||||
const prefs = this._params!.preferences;
|
||||
return VIEW_GROUPS.map((group) => {
|
||||
const cards = ENERGY_CARD_CATALOG.filter((c) => c.view === group.view);
|
||||
// Hide the whole group when none of its cards apply to the current config.
|
||||
if (!cards.some((c) => c.isApplicable(prefs))) {
|
||||
return nothing;
|
||||
}
|
||||
return html`
|
||||
<ha-expansion-panel
|
||||
outlined
|
||||
expanded
|
||||
.header=${this._i18n.localize(group.labelKey)}
|
||||
>
|
||||
<div class="cards">
|
||||
${cards.map((card) => this._renderCardRow(card))}
|
||||
</div>
|
||||
</ha-expansion-panel>
|
||||
`;
|
||||
});
|
||||
}
|
||||
|
||||
private _renderCardRow(card: EnergyCardCatalogEntry) {
|
||||
const applicable = card.isApplicable(this._params!.preferences);
|
||||
const label = this._i18n.localize(card.labelKey);
|
||||
const rowId = `row-${card.key}`;
|
||||
return html`
|
||||
<ha-settings-row slim id=${rowId}>
|
||||
<span slot="heading" class=${applicable ? "" : "disabled"}
|
||||
>${label}</span
|
||||
>
|
||||
<ha-switch
|
||||
.checked=${applicable && !this._hidden!.has(card.key)}
|
||||
.disabled=${!applicable}
|
||||
.ariaLabel=${label}
|
||||
data-card-key=${card.key}
|
||||
@change=${this._toggleCard}
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
${applicable
|
||||
? nothing
|
||||
: html`
|
||||
<ha-tooltip .for=${rowId} placement="top">
|
||||
${this._i18n.localize(
|
||||
"ui.panel.config.energy.customise.unavailable"
|
||||
)}
|
||||
</ha-tooltip>
|
||||
`}
|
||||
`;
|
||||
}
|
||||
|
||||
private _toggleCard = (ev: Event): void => {
|
||||
const target = ev.currentTarget as HaSwitch;
|
||||
const cardKey = target.dataset.cardKey;
|
||||
if (!cardKey) {
|
||||
return;
|
||||
}
|
||||
const next = new Set(this._hidden);
|
||||
if (target.checked) {
|
||||
next.delete(cardKey);
|
||||
} else {
|
||||
next.add(cardKey);
|
||||
}
|
||||
this._hidden = next;
|
||||
};
|
||||
|
||||
private async _save(): Promise<void> {
|
||||
if (!this._hidden) {
|
||||
return;
|
||||
}
|
||||
this._submitting = true;
|
||||
try {
|
||||
const hidden = Array.from(this._hidden);
|
||||
await saveFrontendSystemData(this._connection.connection, "energy", {
|
||||
hidden_cards: hidden.length ? hidden : undefined,
|
||||
});
|
||||
this._params?.saveCallback?.();
|
||||
this.closeDialog();
|
||||
} catch (_err) {
|
||||
showToast(this, {
|
||||
message: this._i18n.localize(
|
||||
"ui.panel.config.energy.customise.save_failed"
|
||||
),
|
||||
duration: 0,
|
||||
dismissable: true,
|
||||
});
|
||||
} finally {
|
||||
this._submitting = false;
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-dialog {
|
||||
--dialog-content-padding: var(--ha-space-2) var(--ha-space-6);
|
||||
}
|
||||
.loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: var(--ha-space-6);
|
||||
}
|
||||
span.disabled {
|
||||
color: var(--disabled-text-color);
|
||||
}
|
||||
.groups {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--ha-space-4);
|
||||
}
|
||||
ha-expansion-panel {
|
||||
display: block;
|
||||
--expansion-panel-content-padding: 0;
|
||||
--expansion-panel-summary-padding: 0 var(--ha-space-4);
|
||||
border-radius: var(--ha-border-radius-md);
|
||||
--ha-card-border-radius: var(--ha-border-radius-md);
|
||||
}
|
||||
.cards {
|
||||
padding: 0 var(--ha-space-4);
|
||||
}
|
||||
.cards ha-settings-row {
|
||||
min-height: 48px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-energy-customise": DialogEnergyCustomise;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { EnergyPreferences } from "../../../../data/energy";
|
||||
|
||||
export interface EnergyCustomiseDialogParams {
|
||||
preferences: EnergyPreferences;
|
||||
// Called after a successful save (e.g. to show a toast on the page).
|
||||
saveCallback?: () => void;
|
||||
}
|
||||
|
||||
export const loadEnergyCustomiseDialog = () =>
|
||||
import("./dialog-energy-customise");
|
||||
|
||||
export const showEnergyCustomiseDialog = (
|
||||
element: HTMLElement,
|
||||
params: EnergyCustomiseDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-energy-customise",
|
||||
dialogImport: loadEnergyCustomiseDialog,
|
||||
dialogParams: params,
|
||||
});
|
||||
};
|
||||
@@ -1,11 +1,5 @@
|
||||
import "../../../layouts/hass-error-screen";
|
||||
import {
|
||||
mdiDownload,
|
||||
mdiFire,
|
||||
mdiLightningBolt,
|
||||
mdiViewDashboardEdit,
|
||||
mdiWater,
|
||||
} from "@mdi/js";
|
||||
import { mdiDownload, mdiFire, mdiLightningBolt, mdiWater } from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
@@ -37,9 +31,7 @@ import "./components/ha-energy-battery-settings";
|
||||
import "./components/ha-energy-gas-settings";
|
||||
import "./components/ha-energy-water-settings";
|
||||
import { fileDownload } from "../../../util/file_download";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import type { PageNavigation } from "../../../layouts/hass-tabs-subpage";
|
||||
import { showEnergyCustomiseDialog } from "./dialogs/show-dialog-energy-customise";
|
||||
|
||||
const INITIAL_CONFIG: EnergyPreferences = {
|
||||
energy_sources: [],
|
||||
@@ -132,22 +124,14 @@ class HaConfigEnergy extends LitElement {
|
||||
.route=${this.route}
|
||||
.tabs=${TABS}
|
||||
>
|
||||
<div slot="toolbar-icon" class="toolbar-icons">
|
||||
<ha-icon-button
|
||||
.path=${mdiViewDashboardEdit}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.energy.customise.toolbar_action"
|
||||
)}
|
||||
@click=${this._customise}
|
||||
></ha-icon-button>
|
||||
<ha-icon-button
|
||||
.path=${mdiDownload}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.devices.download_diagnostics"
|
||||
)}
|
||||
@click=${this._downloadDiagnostics}
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
<ha-icon-button
|
||||
slot="toolbar-icon"
|
||||
.path=${mdiDownload}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.devices.download_diagnostics"
|
||||
)}
|
||||
@click=${this._downloadDiagnostics}
|
||||
></ha-icon-button>
|
||||
<ha-alert>
|
||||
${this.hass.localize("ui.panel.config.energy.new_device_info")}
|
||||
</ha-alert>
|
||||
@@ -270,20 +254,6 @@ class HaConfigEnergy extends LitElement {
|
||||
this._statsMetadata = statsMetadata;
|
||||
}
|
||||
|
||||
private _customise() {
|
||||
if (!this._preferences) {
|
||||
return;
|
||||
}
|
||||
showEnergyCustomiseDialog(this, {
|
||||
preferences: this._preferences,
|
||||
saveCallback: () => {
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.panel.config.energy.customise.saved"),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private async _downloadDiagnostics() {
|
||||
const data = {
|
||||
version: this.hass.config.version,
|
||||
@@ -315,10 +285,6 @@ class HaConfigEnergy extends LitElement {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.toolbar-icons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.content {
|
||||
padding: 0 var(--ha-space-5);
|
||||
max-width: 1040px;
|
||||
|
||||
@@ -1014,6 +1014,7 @@ export class HaConfigEntities extends LitElement {
|
||||
@expanded-changed=${this._filterExpanded}
|
||||
></ha-filter-domains>
|
||||
<ha-filter-integrations
|
||||
.hass=${this.hass}
|
||||
.value=${this._filters["ha-filter-integrations"]}
|
||||
@data-table-filter-changed=${this._filterChanged}
|
||||
slot="filter-pane"
|
||||
|
||||
@@ -46,6 +46,7 @@ class ConfigNetwork extends LitElement {
|
||||
</p>
|
||||
<ha-network
|
||||
@network-config-changed=${this._configChanged}
|
||||
.hass=${this.hass}
|
||||
.networkConfig=${this._networkConfig}
|
||||
></ha-network>
|
||||
</div>
|
||||
|
||||
@@ -41,10 +41,7 @@ import "../../../components/ha-dropdown-item";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-list";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import {
|
||||
fullEntitiesContext,
|
||||
type RelatedContextItem,
|
||||
} from "../../../data/context";
|
||||
import { fullEntitiesContext } from "../../../data/context";
|
||||
import type { DeviceRegistryEntry } from "../../../data/device/device_registry";
|
||||
import type { EntityRegistryEntry } from "../../../data/entity/entity_registry";
|
||||
import { updateEntityRegistryEntry } from "../../../data/entity/entity_registry";
|
||||
@@ -153,8 +150,6 @@ export class HaSceneEditor extends PreventUnsavedMixin(
|
||||
|
||||
private _entityRegistryUpdate?: EntityRegistryUpdate;
|
||||
|
||||
private _relatedContext?: RelatedContextItem;
|
||||
|
||||
private _newSceneId?: string;
|
||||
|
||||
private _entityRegCreated?: (
|
||||
@@ -658,40 +653,6 @@ export class HaSceneEditor extends PreventUnsavedMixin(
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
changedProps.has("sceneId") ||
|
||||
changedProps.has("_scene") ||
|
||||
changedProps.has("_registryEntry")
|
||||
) {
|
||||
this._setRelatedContext();
|
||||
}
|
||||
}
|
||||
|
||||
private _setRelatedContext(): void {
|
||||
const context: RelatedContextItem | undefined = this.sceneId
|
||||
? this._registryEntry?.area_id
|
||||
? {
|
||||
itemType: "area",
|
||||
itemId: this._registryEntry.area_id,
|
||||
}
|
||||
: this._scene
|
||||
? {
|
||||
itemType: "scene",
|
||||
itemId: this._scene.entity_id,
|
||||
}
|
||||
: undefined
|
||||
: undefined;
|
||||
|
||||
if (
|
||||
context?.itemType === this._relatedContext?.itemType &&
|
||||
context?.itemId === this._relatedContext?.itemId
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._relatedContext = context;
|
||||
fireEvent(this, "hass-related-context", context);
|
||||
}
|
||||
|
||||
private _handleMenuAction(ev: HaDropdownSelectEvent) {
|
||||
|
||||
@@ -304,6 +304,7 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
|
||||
></ha-switch>
|
||||
</ha-md-list-item>
|
||||
<ha-aliases-editor
|
||||
.hass=${this.hass}
|
||||
.aliases=${(this._aliases ?? this.entry.aliases).filter(
|
||||
(a): a is string => a !== null
|
||||
)}
|
||||
|
||||
@@ -6,8 +6,6 @@ import "../../components/ha-alert";
|
||||
import "../../components/ha-icon-button-arrow-prev";
|
||||
import "../../components/ha-menu-button";
|
||||
import "../../components/ha-top-app-bar-fixed";
|
||||
import type { EnergyFrontendSystemData } from "../../data/frontend";
|
||||
import { fetchFrontendSystemData } from "../../data/frontend";
|
||||
import type { LovelaceConfig } from "../../data/lovelace/config/types";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import type { HomeAssistant, PanelInfo } from "../../types";
|
||||
@@ -28,8 +26,6 @@ class PanelEnergy extends LitElement {
|
||||
|
||||
@state() private _lovelace?: Lovelace;
|
||||
|
||||
@state() private _config: EnergyFrontendSystemData = {};
|
||||
|
||||
@property({ attribute: false }) public route?: {
|
||||
path: string;
|
||||
prefix: string;
|
||||
@@ -62,23 +58,10 @@ class PanelEnergy extends LitElement {
|
||||
await Promise.all([
|
||||
this.hass.loadFragmentTranslation("lovelace"),
|
||||
this.hass.loadFragmentTranslation("energy"),
|
||||
this._loadSystemData(),
|
||||
]);
|
||||
this._loadConfig();
|
||||
}
|
||||
|
||||
private async _loadSystemData() {
|
||||
try {
|
||||
const data = await fetchFrontendSystemData(
|
||||
this.hass.connection,
|
||||
"energy"
|
||||
);
|
||||
this._config = data || {};
|
||||
} catch (_err) {
|
||||
this._config = {};
|
||||
}
|
||||
}
|
||||
|
||||
private async _loadConfig() {
|
||||
try {
|
||||
this._error = undefined;
|
||||
@@ -111,7 +94,6 @@ class PanelEnergy extends LitElement {
|
||||
this.route?.path === "/now"
|
||||
? DEFAULT_POWER_COLLECTION_KEY
|
||||
: undefined,
|
||||
hidden_cards: this._config.hidden_cards,
|
||||
},
|
||||
},
|
||||
this.hass
|
||||
@@ -182,8 +164,7 @@ class PanelEnergy extends LitElement {
|
||||
navigate(`/config/energy/${tab}?historyBack=1`);
|
||||
}
|
||||
|
||||
private async _reloadConfig() {
|
||||
await this._loadSystemData();
|
||||
private _reloadConfig() {
|
||||
this._loadConfig();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,296 +0,0 @@
|
||||
import type { LocalizeKeys } from "../../../common/translations/localize";
|
||||
import type {
|
||||
EnergyPreferences,
|
||||
GridSourceTypeEnergyPreference,
|
||||
} from "../../../data/energy";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
|
||||
/** Strategy config shared by the per-view energy strategies. */
|
||||
export interface EnergyViewStrategyConfig extends LovelaceStrategyConfig {
|
||||
collection_key?: string;
|
||||
hidden_cards?: string[];
|
||||
}
|
||||
|
||||
export type EnergyViewPath =
|
||||
| "overview"
|
||||
| "electricity"
|
||||
| "gas"
|
||||
| "water"
|
||||
| "now";
|
||||
|
||||
// --- Applicability helpers -------------------------------------------------
|
||||
// These mirror, one-to-one, the conditions the individual view strategies use
|
||||
// to decide whether to emit a card. The catalog and the strategies must agree
|
||||
// on what "applicable" means, so the conditions live here and are reused.
|
||||
|
||||
export const hasGridSource = (prefs: EnergyPreferences): boolean =>
|
||||
prefs.energy_sources.some(
|
||||
(source): source is GridSourceTypeEnergyPreference =>
|
||||
source.type === "grid" &&
|
||||
(!!source.stat_energy_from || !!source.stat_energy_to)
|
||||
);
|
||||
|
||||
export const hasReturn = (prefs: EnergyPreferences): boolean =>
|
||||
prefs.energy_sources.some(
|
||||
(source) => source.type === "grid" && !!source.stat_energy_to
|
||||
);
|
||||
|
||||
export const hasSolar = (prefs: EnergyPreferences): boolean =>
|
||||
prefs.energy_sources.some((source) => source.type === "solar");
|
||||
|
||||
export const hasBattery = (prefs: EnergyPreferences): boolean =>
|
||||
prefs.energy_sources.some((source) => source.type === "battery");
|
||||
|
||||
export const hasGasSource = (prefs: EnergyPreferences): boolean =>
|
||||
prefs.energy_sources.some((source) => source.type === "gas");
|
||||
|
||||
export const hasWaterSource = (prefs: EnergyPreferences): boolean =>
|
||||
prefs.energy_sources.some((source) => source.type === "water");
|
||||
|
||||
export const hasWaterDevices = (prefs: EnergyPreferences): boolean =>
|
||||
(prefs.device_consumption_water?.length ?? 0) > 0;
|
||||
|
||||
export const hasDeviceConsumption = (prefs: EnergyPreferences): boolean =>
|
||||
prefs.device_consumption.length > 0;
|
||||
|
||||
export const hasPowerSources = (prefs: EnergyPreferences): boolean =>
|
||||
prefs.energy_sources.some((source) => {
|
||||
if (source.type === "solar" && source.stat_rate) return true;
|
||||
if (source.type === "battery" && source.stat_rate) return true;
|
||||
if (source.type === "grid") {
|
||||
return !!source.stat_rate || !!source.power_config;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
export const hasPowerDevices = (prefs: EnergyPreferences): boolean =>
|
||||
prefs.device_consumption.some((device) => device.stat_rate);
|
||||
|
||||
export const hasPowerWaterDevices = (prefs: EnergyPreferences): boolean =>
|
||||
(prefs.device_consumption_water ?? []).some((device) => device.stat_rate);
|
||||
|
||||
// --- Card catalog ----------------------------------------------------------
|
||||
|
||||
export interface EnergyCardCatalogEntry {
|
||||
/** Stable identifier and storage token: `<view>.<cardType>`. */
|
||||
key: string;
|
||||
view: EnergyViewPath;
|
||||
/** Localize key for the label shown in the customise dialog. */
|
||||
labelKey: LocalizeKeys;
|
||||
/** Whether this card is emitted for the given preferences. */
|
||||
isApplicable: (prefs: EnergyPreferences) => boolean;
|
||||
}
|
||||
|
||||
export const energyCardKey = (view: EnergyViewPath, cardType: string): string =>
|
||||
`${view}.${cardType}`;
|
||||
|
||||
const entry = (
|
||||
view: EnergyViewPath,
|
||||
cardType: string,
|
||||
labelKey: LocalizeKeys,
|
||||
isApplicable: (prefs: EnergyPreferences) => boolean
|
||||
): EnergyCardCatalogEntry => ({
|
||||
key: energyCardKey(view, cardType),
|
||||
view,
|
||||
labelKey,
|
||||
isApplicable,
|
||||
});
|
||||
|
||||
export const ENERGY_CARD_CATALOG: readonly EnergyCardCatalogEntry[] = [
|
||||
// --- Overview ---
|
||||
entry(
|
||||
"overview",
|
||||
"energy-distribution",
|
||||
"ui.panel.energy.cards.energy_distribution_title",
|
||||
(p) => hasGridSource(p) || hasBattery(p) || hasSolar(p)
|
||||
),
|
||||
entry(
|
||||
"overview",
|
||||
"energy-sources-table",
|
||||
"ui.panel.energy.cards.energy_sources_table_title",
|
||||
(p) => p.energy_sources.length > 0
|
||||
),
|
||||
entry(
|
||||
"overview",
|
||||
"power-sources-graph",
|
||||
"ui.panel.energy.cards.power_sources_graph_title",
|
||||
(p) => hasPowerSources(p)
|
||||
),
|
||||
entry(
|
||||
"overview",
|
||||
"energy-usage-graph",
|
||||
"ui.panel.energy.cards.energy_usage_graph_title",
|
||||
(p) => hasGridSource(p) || hasBattery(p)
|
||||
),
|
||||
entry(
|
||||
"overview",
|
||||
"energy-gas-graph",
|
||||
"ui.panel.energy.cards.energy_gas_graph_title",
|
||||
(p) => hasGasSource(p)
|
||||
),
|
||||
// One toggle gates the water row, which renders energy-water-graph (sources)
|
||||
// or, with only water devices, water-sankey.
|
||||
entry(
|
||||
"overview",
|
||||
"energy-water-graph",
|
||||
"ui.panel.energy.cards.energy_water_graph_title",
|
||||
(p) => hasWaterSource(p) || hasWaterDevices(p)
|
||||
),
|
||||
|
||||
// --- Electricity ---
|
||||
entry(
|
||||
"electricity",
|
||||
"energy-distribution",
|
||||
"ui.panel.energy.cards.energy_distribution_title",
|
||||
(p) => hasGridSource(p) || hasBattery(p) || hasSolar(p)
|
||||
),
|
||||
entry(
|
||||
"electricity",
|
||||
"energy-grid-balance",
|
||||
"ui.panel.energy.cards.energy_grid_balance_title",
|
||||
(p) => hasGridSource(p) && hasReturn(p)
|
||||
),
|
||||
entry(
|
||||
"electricity",
|
||||
"energy-grid-neutrality-gauge",
|
||||
"ui.panel.energy.cards.energy_grid_neutrality_gauge_title",
|
||||
(p) => hasReturn(p)
|
||||
),
|
||||
entry(
|
||||
"electricity",
|
||||
"energy-solar-consumed-gauge",
|
||||
"ui.panel.energy.cards.energy_solar_consumed_gauge_title",
|
||||
(p) => hasSolar(p) && hasReturn(p)
|
||||
),
|
||||
entry(
|
||||
"electricity",
|
||||
"energy-self-sufficiency-gauge",
|
||||
"ui.panel.energy.cards.energy_self_sufficiency_gauge_title",
|
||||
(p) => hasSolar(p) && hasGridSource(p)
|
||||
),
|
||||
entry(
|
||||
"electricity",
|
||||
"energy-carbon-consumed-gauge",
|
||||
"ui.panel.energy.cards.energy_carbon_consumed_gauge_title",
|
||||
(p) => hasGridSource(p)
|
||||
),
|
||||
entry(
|
||||
"electricity",
|
||||
"energy-usage-graph",
|
||||
"ui.panel.energy.cards.energy_usage_graph_title",
|
||||
(p) => hasGridSource(p) || hasBattery(p)
|
||||
),
|
||||
entry(
|
||||
"electricity",
|
||||
"energy-solar-graph",
|
||||
"ui.panel.energy.cards.energy_solar_graph_title",
|
||||
(p) => hasSolar(p)
|
||||
),
|
||||
entry(
|
||||
"electricity",
|
||||
"energy-sources-table",
|
||||
"ui.panel.energy.cards.energy_sources_table_title",
|
||||
(p) => hasGridSource(p) || hasSolar(p) || hasBattery(p)
|
||||
),
|
||||
entry(
|
||||
"electricity",
|
||||
"energy-devices-detail-graph",
|
||||
"ui.panel.energy.cards.energy_devices_detail_graph_title",
|
||||
(p) => hasDeviceConsumption(p)
|
||||
),
|
||||
entry(
|
||||
"electricity",
|
||||
"energy-devices-graph",
|
||||
"ui.panel.energy.cards.energy_devices_graph_title",
|
||||
(p) => hasDeviceConsumption(p)
|
||||
),
|
||||
entry(
|
||||
"electricity",
|
||||
"energy-sankey",
|
||||
"ui.panel.energy.cards.energy_sankey_title",
|
||||
(p) => hasDeviceConsumption(p)
|
||||
),
|
||||
|
||||
// --- Gas ---
|
||||
entry(
|
||||
"gas",
|
||||
"energy-gas-graph",
|
||||
"ui.panel.energy.cards.energy_gas_graph_title",
|
||||
(p) => hasGasSource(p)
|
||||
),
|
||||
entry(
|
||||
"gas",
|
||||
"energy-sources-table",
|
||||
"ui.panel.energy.cards.energy_sources_table_title",
|
||||
(p) => hasGasSource(p)
|
||||
),
|
||||
|
||||
// --- Water ---
|
||||
entry(
|
||||
"water",
|
||||
"energy-water-graph",
|
||||
"ui.panel.energy.cards.energy_water_graph_title",
|
||||
(p) => hasWaterSource(p)
|
||||
),
|
||||
entry(
|
||||
"water",
|
||||
"energy-sources-table",
|
||||
"ui.panel.energy.cards.energy_sources_table_title",
|
||||
(p) => hasWaterSource(p)
|
||||
),
|
||||
entry(
|
||||
"water",
|
||||
"water-sankey",
|
||||
"ui.panel.energy.cards.water_sankey_title",
|
||||
(p) => hasWaterDevices(p)
|
||||
),
|
||||
|
||||
// --- Now (power) ---
|
||||
entry(
|
||||
"now",
|
||||
"power-sources-graph",
|
||||
"ui.panel.energy.cards.power_sources_graph_title",
|
||||
(p) => hasPowerSources(p)
|
||||
),
|
||||
entry(
|
||||
"now",
|
||||
"power-sankey",
|
||||
"ui.panel.energy.cards.power_sankey_title",
|
||||
(p) => hasPowerDevices(p)
|
||||
),
|
||||
entry(
|
||||
"now",
|
||||
"water-flow-sankey",
|
||||
"ui.panel.energy.cards.water_flow_sankey_title",
|
||||
(p) => hasPowerWaterDevices(p)
|
||||
),
|
||||
];
|
||||
|
||||
// --- Lookup helpers --------------------------------------------------------
|
||||
|
||||
export const isEnergyCardHidden = (
|
||||
view: EnergyViewPath,
|
||||
cardType: string,
|
||||
hidden: string[] | undefined
|
||||
): boolean => !!hidden?.includes(energyCardKey(view, cardType));
|
||||
|
||||
/** Keys of all catalog cards that apply to the given preferences for a view. */
|
||||
export const applicableEnergyCardKeys = (
|
||||
view: EnergyViewPath,
|
||||
prefs: EnergyPreferences
|
||||
): string[] =>
|
||||
ENERGY_CARD_CATALOG.filter(
|
||||
(c) => c.view === view && c.isApplicable(prefs)
|
||||
).map((c) => c.key);
|
||||
|
||||
/** True when a view has applicable cards but every one of them is hidden. */
|
||||
export const isEnergyViewEmpty = (
|
||||
view: EnergyViewPath,
|
||||
prefs: EnergyPreferences,
|
||||
hidden: string[] | undefined
|
||||
): boolean => {
|
||||
const applicable = applicableEnergyCardKeys(view, prefs);
|
||||
return (
|
||||
applicable.length > 0 && applicable.every((key) => hidden?.includes(key))
|
||||
);
|
||||
};
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
import type { EnergyPreferences } from "../../../data/energy";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import type { LovelaceConfig } from "../../../data/lovelace/config/types";
|
||||
import type { LovelaceStrategyViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { LocalizeKeys } from "../../../common/translations/localize";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceStrategyDependency } from "../../lovelace/strategies/types";
|
||||
@@ -15,8 +15,6 @@ import {
|
||||
DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
DEFAULT_POWER_COLLECTION_KEY,
|
||||
} from "../constants";
|
||||
import type { EnergyViewPath } from "./energy-cards";
|
||||
import { isEnergyViewEmpty } from "./energy-cards";
|
||||
|
||||
const OVERVIEW_VIEW = {
|
||||
path: "overview",
|
||||
@@ -24,7 +22,7 @@ const OVERVIEW_VIEW = {
|
||||
type: "energy-overview",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
},
|
||||
} as LovelaceStrategyViewConfig;
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const ENERGY_VIEW = {
|
||||
path: "electricity",
|
||||
@@ -32,7 +30,7 @@ const ENERGY_VIEW = {
|
||||
type: "energy",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
},
|
||||
} as LovelaceStrategyViewConfig;
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const WATER_VIEW = {
|
||||
path: "water",
|
||||
@@ -40,7 +38,7 @@ const WATER_VIEW = {
|
||||
type: "water",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
},
|
||||
} as LovelaceStrategyViewConfig;
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const GAS_VIEW = {
|
||||
path: "gas",
|
||||
@@ -48,7 +46,7 @@ const GAS_VIEW = {
|
||||
type: "gas",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
},
|
||||
} as LovelaceStrategyViewConfig;
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const POWER_VIEW = {
|
||||
path: "now",
|
||||
@@ -56,7 +54,7 @@ const POWER_VIEW = {
|
||||
type: "power",
|
||||
collection_key: DEFAULT_POWER_COLLECTION_KEY,
|
||||
},
|
||||
} as LovelaceStrategyViewConfig;
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const WIZARD_VIEW = {
|
||||
type: "panel",
|
||||
@@ -67,7 +65,6 @@ const WIZARD_VIEW = {
|
||||
export interface EnergyDashboardStrategyConfig extends LovelaceStrategyConfig {
|
||||
type: "energy";
|
||||
default_collection?: string;
|
||||
hidden_cards?: string[];
|
||||
}
|
||||
|
||||
@customElement("energy-dashboard-strategy")
|
||||
@@ -118,42 +115,28 @@ export class EnergyDashboardStrategy extends ReactiveElement {
|
||||
|
||||
const hasDeviceConsumption = prefs.device_consumption.length > 0;
|
||||
|
||||
const hidden = _config.hidden_cards;
|
||||
|
||||
const candidateViews: LovelaceStrategyViewConfig[] = [];
|
||||
const views: LovelaceViewConfig[] = [];
|
||||
if (hasEnergy || hasDeviceConsumption) {
|
||||
candidateViews.push(ENERGY_VIEW);
|
||||
views.push(ENERGY_VIEW);
|
||||
}
|
||||
if (hasGas) {
|
||||
candidateViews.push(GAS_VIEW);
|
||||
views.push(GAS_VIEW);
|
||||
}
|
||||
if (hasWater) {
|
||||
candidateViews.push(WATER_VIEW);
|
||||
views.push(WATER_VIEW);
|
||||
}
|
||||
if (hasPower) {
|
||||
candidateViews.push(POWER_VIEW);
|
||||
views.push(POWER_VIEW);
|
||||
}
|
||||
if (
|
||||
hasPowerSource ||
|
||||
[hasEnergy, hasGas, hasWater].filter(Boolean).length > 1
|
||||
) {
|
||||
candidateViews.unshift(OVERVIEW_VIEW);
|
||||
views.unshift(OVERVIEW_VIEW);
|
||||
}
|
||||
|
||||
// Drop a view (tab) when every card it would render has been hidden, so we
|
||||
// don't show an empty tab. Keep at least one view so the dashboard never
|
||||
// renders blank and the customize entry stays reachable.
|
||||
let views = candidateViews.filter(
|
||||
(view) => !isEnergyViewEmpty(view.path as EnergyViewPath, prefs, hidden)
|
||||
);
|
||||
if (views.length === 0) {
|
||||
views = candidateViews;
|
||||
}
|
||||
|
||||
return {
|
||||
views: views.map((view) => ({
|
||||
...view,
|
||||
strategy: { ...view.strategy, hidden_cards: hidden },
|
||||
title:
|
||||
view.title ||
|
||||
hass.localize(`ui.panel.energy.title.${view.path}` as LocalizeKeys),
|
||||
|
||||
@@ -4,22 +4,20 @@ import type { GridSourceTypeEnergyPreference } from "../../../data/energy";
|
||||
import { getEnergyDataCollection } from "../../../data/energy";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import type { LovelaceStrategyDependency } from "../../lovelace/strategies/types";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../constants";
|
||||
import type { EnergyViewStrategyConfig } from "./energy-cards";
|
||||
import { isEnergyCardHidden } from "./energy-cards";
|
||||
|
||||
@customElement("energy-overview-view-strategy")
|
||||
export class EnergyOverviewViewStrategy extends ReactiveElement {
|
||||
static registryDependencies: readonly LovelaceStrategyDependency[] = [];
|
||||
|
||||
static async generate(
|
||||
_config: EnergyViewStrategyConfig,
|
||||
_config: LovelaceStrategyConfig,
|
||||
hass: HomeAssistant
|
||||
): Promise<LovelaceViewConfig> {
|
||||
const collectionKey =
|
||||
_config.collection_key || DEFAULT_ENERGY_COLLECTION_KEY;
|
||||
const hidden = _config.hidden_cards;
|
||||
|
||||
const view: LovelaceViewConfig = {
|
||||
type: "sections",
|
||||
@@ -78,10 +76,7 @@ export class EnergyOverviewViewStrategy extends ReactiveElement {
|
||||
return false;
|
||||
});
|
||||
|
||||
if (
|
||||
(hasGrid || hasBattery || hasSolar) &&
|
||||
!isEnergyCardHidden("overview", "energy-distribution", hidden)
|
||||
) {
|
||||
if (hasGrid || hasBattery || hasSolar) {
|
||||
view.sections!.push({
|
||||
type: "grid",
|
||||
cards: [
|
||||
@@ -96,10 +91,7 @@ export class EnergyOverviewViewStrategy extends ReactiveElement {
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
prefs.energy_sources.length &&
|
||||
!isEnergyCardHidden("overview", "energy-sources-table", hidden)
|
||||
) {
|
||||
if (prefs.energy_sources.length) {
|
||||
view.sections!.push({
|
||||
type: "grid",
|
||||
cards: [
|
||||
@@ -115,10 +107,7 @@ export class EnergyOverviewViewStrategy extends ReactiveElement {
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
hasPowerSources &&
|
||||
!isEnergyCardHidden("overview", "power-sources-graph", hidden)
|
||||
) {
|
||||
if (hasPowerSources) {
|
||||
view.sections!.push({
|
||||
type: "grid",
|
||||
cards: [
|
||||
@@ -134,10 +123,7 @@ export class EnergyOverviewViewStrategy extends ReactiveElement {
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
(hasGrid || hasBattery) &&
|
||||
!isEnergyCardHidden("overview", "energy-usage-graph", hidden)
|
||||
) {
|
||||
if (hasGrid || hasBattery) {
|
||||
view.sections!.push({
|
||||
type: "grid",
|
||||
cards: [
|
||||
@@ -152,7 +138,7 @@ export class EnergyOverviewViewStrategy extends ReactiveElement {
|
||||
});
|
||||
}
|
||||
|
||||
if (hasGas && !isEnergyCardHidden("overview", "energy-gas-graph", hidden)) {
|
||||
if (hasGas) {
|
||||
view.sections!.push({
|
||||
type: "grid",
|
||||
cards: [
|
||||
@@ -167,10 +153,7 @@ export class EnergyOverviewViewStrategy extends ReactiveElement {
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
(hasWaterSources || hasWaterDevices) &&
|
||||
!isEnergyCardHidden("overview", "energy-water-graph", hidden)
|
||||
) {
|
||||
if (hasWaterSources || hasWaterDevices) {
|
||||
view.sections!.push({
|
||||
type: "grid",
|
||||
cards: [
|
||||
|
||||
@@ -3,11 +3,10 @@ import { customElement } from "lit/decorators";
|
||||
import type { GridSourceTypeEnergyPreference } from "../../../data/energy";
|
||||
import { getEnergyDataCollection } from "../../../data/energy";
|
||||
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../constants";
|
||||
import type { EnergyViewStrategyConfig } from "./energy-cards";
|
||||
import { isEnergyCardHidden } from "./energy-cards";
|
||||
import { shouldShowFloorsAndAreas } from "./show-floors-and-areas";
|
||||
import {
|
||||
LARGE_SCREEN_CONDITION,
|
||||
@@ -20,12 +19,11 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
static registryDependencies: readonly LovelaceStrategyDependency[] = [];
|
||||
|
||||
static async generate(
|
||||
_config: EnergyViewStrategyConfig,
|
||||
_config: LovelaceStrategyConfig,
|
||||
hass: HomeAssistant
|
||||
): Promise<LovelaceViewConfig> {
|
||||
const collectionKey =
|
||||
_config.collection_key || DEFAULT_ENERGY_COLLECTION_KEY;
|
||||
const hidden = _config.hidden_cards;
|
||||
|
||||
const view: LovelaceViewConfig = {
|
||||
type: "sections",
|
||||
@@ -80,10 +78,7 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
const gaugeCards: LovelaceCardConfig[] = [];
|
||||
const sidebarSection = view.sidebar!.sections![0];
|
||||
|
||||
if (
|
||||
(hasGrid || hasBattery || hasSolar) &&
|
||||
!isEnergyCardHidden("electricity", "energy-distribution", hidden)
|
||||
) {
|
||||
if (hasGrid || hasBattery || hasSolar) {
|
||||
const distributionCard = {
|
||||
title: hass.localize("ui.panel.energy.cards.energy_distribution_title"),
|
||||
type: "energy-distribution",
|
||||
@@ -99,11 +94,7 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
}
|
||||
|
||||
// Only include if we have both grid import and export configured
|
||||
if (
|
||||
hasGrid &&
|
||||
hasReturn &&
|
||||
!isEnergyCardHidden("electricity", "energy-grid-balance", hidden)
|
||||
) {
|
||||
if (hasGrid && hasReturn) {
|
||||
const gridResultCard = {
|
||||
type: "energy-grid-balance",
|
||||
collection_key: collectionKey,
|
||||
@@ -118,10 +109,7 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
}
|
||||
|
||||
// Only include if we have a grid source & return.
|
||||
if (
|
||||
hasReturn &&
|
||||
!isEnergyCardHidden("electricity", "energy-grid-neutrality-gauge", hidden)
|
||||
) {
|
||||
if (hasReturn) {
|
||||
const card = {
|
||||
type: "energy-grid-neutrality-gauge",
|
||||
collection_key: collectionKey,
|
||||
@@ -131,28 +119,14 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
|
||||
// Only include if we have a solar source.
|
||||
if (hasSolar) {
|
||||
if (
|
||||
hasReturn &&
|
||||
!isEnergyCardHidden(
|
||||
"electricity",
|
||||
"energy-solar-consumed-gauge",
|
||||
hidden
|
||||
)
|
||||
) {
|
||||
if (hasReturn) {
|
||||
const card = {
|
||||
type: "energy-solar-consumed-gauge",
|
||||
collection_key: collectionKey,
|
||||
};
|
||||
gaugeCards.push(card);
|
||||
}
|
||||
if (
|
||||
hasGrid &&
|
||||
!isEnergyCardHidden(
|
||||
"electricity",
|
||||
"energy-self-sufficiency-gauge",
|
||||
hidden
|
||||
)
|
||||
) {
|
||||
if (hasGrid) {
|
||||
const card = {
|
||||
type: "energy-self-sufficiency-gauge",
|
||||
collection_key: collectionKey,
|
||||
@@ -162,10 +136,7 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
}
|
||||
|
||||
// Only include if we have a grid
|
||||
if (
|
||||
hasGrid &&
|
||||
!isEnergyCardHidden("electricity", "energy-carbon-consumed-gauge", hidden)
|
||||
) {
|
||||
if (hasGrid) {
|
||||
const card = {
|
||||
type: "energy-carbon-consumed-gauge",
|
||||
collection_key: collectionKey,
|
||||
@@ -200,10 +171,7 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
});
|
||||
|
||||
// Only include if we have a grid or battery.
|
||||
if (
|
||||
(hasGrid || hasBattery) &&
|
||||
!isEnergyCardHidden("electricity", "energy-usage-graph", hidden)
|
||||
) {
|
||||
if (hasGrid || hasBattery) {
|
||||
mainCards.push({
|
||||
title: hass.localize("ui.panel.energy.cards.energy_usage_graph_title"),
|
||||
type: "energy-usage-graph",
|
||||
@@ -213,10 +181,7 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
}
|
||||
|
||||
// Only include if we have a solar source.
|
||||
if (
|
||||
hasSolar &&
|
||||
!isEnergyCardHidden("electricity", "energy-solar-graph", hidden)
|
||||
) {
|
||||
if (hasSolar) {
|
||||
mainCards.push({
|
||||
title: hass.localize("ui.panel.energy.cards.energy_solar_graph_title"),
|
||||
type: "energy-solar-graph",
|
||||
@@ -225,10 +190,7 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
(hasGrid || hasSolar || hasBattery) &&
|
||||
!isEnergyCardHidden("electricity", "energy-sources-table", hidden)
|
||||
) {
|
||||
if (hasGrid || hasSolar || hasBattery) {
|
||||
mainCards.push({
|
||||
title: hass.localize(
|
||||
"ui.panel.energy.cards.energy_sources_table_title"
|
||||
@@ -242,47 +204,35 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
|
||||
// Only include if we have at least 1 device in the config.
|
||||
if (prefs.device_consumption.length) {
|
||||
if (
|
||||
!isEnergyCardHidden(
|
||||
"electricity",
|
||||
"energy-devices-detail-graph",
|
||||
hidden
|
||||
)
|
||||
) {
|
||||
mainCards.push({
|
||||
title: hass.localize(
|
||||
"ui.panel.energy.cards.energy_devices_detail_graph_title"
|
||||
),
|
||||
type: "energy-devices-detail-graph",
|
||||
collection_key: collectionKey,
|
||||
grid_options: { columns: 36 },
|
||||
});
|
||||
}
|
||||
if (!isEnergyCardHidden("electricity", "energy-devices-graph", hidden)) {
|
||||
mainCards.push({
|
||||
title: hass.localize(
|
||||
"ui.panel.energy.cards.energy_devices_graph_title"
|
||||
),
|
||||
type: "energy-devices-graph",
|
||||
collection_key: collectionKey,
|
||||
grid_options: { columns: 36 },
|
||||
});
|
||||
}
|
||||
if (!isEnergyCardHidden("electricity", "energy-sankey", hidden)) {
|
||||
const showFloorsAndAreas = shouldShowFloorsAndAreas(
|
||||
prefs.device_consumption,
|
||||
hass,
|
||||
(d) => d.stat_consumption
|
||||
);
|
||||
mainCards.push({
|
||||
title: hass.localize("ui.panel.energy.cards.energy_sankey_title"),
|
||||
type: "energy-sankey",
|
||||
collection_key: collectionKey,
|
||||
group_by_floor: showFloorsAndAreas,
|
||||
group_by_area: showFloorsAndAreas,
|
||||
grid_options: { columns: 36 },
|
||||
});
|
||||
}
|
||||
const showFloorsAndAreas = shouldShowFloorsAndAreas(
|
||||
prefs.device_consumption,
|
||||
hass,
|
||||
(d) => d.stat_consumption
|
||||
);
|
||||
mainCards.push({
|
||||
title: hass.localize(
|
||||
"ui.panel.energy.cards.energy_devices_detail_graph_title"
|
||||
),
|
||||
type: "energy-devices-detail-graph",
|
||||
collection_key: collectionKey,
|
||||
grid_options: { columns: 36 },
|
||||
});
|
||||
mainCards.push({
|
||||
title: hass.localize(
|
||||
"ui.panel.energy.cards.energy_devices_graph_title"
|
||||
),
|
||||
type: "energy-devices-graph",
|
||||
collection_key: collectionKey,
|
||||
grid_options: { columns: 36 },
|
||||
});
|
||||
mainCards.push({
|
||||
title: hass.localize("ui.panel.energy.cards.energy_sankey_title"),
|
||||
type: "energy-sankey",
|
||||
collection_key: collectionKey,
|
||||
group_by_floor: showFloorsAndAreas,
|
||||
group_by_area: showFloorsAndAreas,
|
||||
grid_options: { columns: 36 },
|
||||
});
|
||||
}
|
||||
|
||||
view.sections!.push({
|
||||
|
||||
@@ -3,9 +3,8 @@ import { customElement } from "lit/decorators";
|
||||
import { getEnergyDataCollection } from "../../../data/energy";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../constants";
|
||||
import type { EnergyViewStrategyConfig } from "./energy-cards";
|
||||
import { isEnergyCardHidden } from "./energy-cards";
|
||||
import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
|
||||
import type { LovelaceStrategyDependency } from "../../lovelace/strategies/types";
|
||||
|
||||
@@ -14,12 +13,11 @@ export class GasViewStrategy extends ReactiveElement {
|
||||
static registryDependencies: readonly LovelaceStrategyDependency[] = [];
|
||||
|
||||
static async generate(
|
||||
_config: EnergyViewStrategyConfig,
|
||||
_config: LovelaceStrategyConfig,
|
||||
hass: HomeAssistant
|
||||
): Promise<LovelaceViewConfig> {
|
||||
const collectionKey =
|
||||
_config.collection_key || DEFAULT_ENERGY_COLLECTION_KEY;
|
||||
const hidden = _config.hidden_cards;
|
||||
|
||||
const view: LovelaceViewConfig = {
|
||||
type: "sections",
|
||||
@@ -62,30 +60,24 @@ export class GasViewStrategy extends ReactiveElement {
|
||||
},
|
||||
});
|
||||
|
||||
if (!isEnergyCardHidden("gas", "energy-gas-graph", hidden)) {
|
||||
section.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.energy_gas_graph_title"),
|
||||
type: "energy-gas-graph",
|
||||
collection_key: collectionKey,
|
||||
grid_options: {
|
||||
columns: 24,
|
||||
},
|
||||
});
|
||||
}
|
||||
section.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.energy_gas_graph_title"),
|
||||
type: "energy-gas-graph",
|
||||
collection_key: collectionKey,
|
||||
grid_options: {
|
||||
columns: 24,
|
||||
},
|
||||
});
|
||||
|
||||
if (!isEnergyCardHidden("gas", "energy-sources-table", hidden)) {
|
||||
section.cards!.push({
|
||||
title: hass.localize(
|
||||
"ui.panel.energy.cards.energy_sources_table_title"
|
||||
),
|
||||
type: "energy-sources-table",
|
||||
collection_key: collectionKey,
|
||||
types: ["gas"],
|
||||
grid_options: {
|
||||
columns: 12,
|
||||
},
|
||||
});
|
||||
}
|
||||
section.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.energy_sources_table_title"),
|
||||
type: "energy-sources-table",
|
||||
collection_key: collectionKey,
|
||||
types: ["gas"],
|
||||
grid_options: {
|
||||
columns: 12,
|
||||
},
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { ReactiveElement } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { getEnergyDataCollection } from "../../../data/energy";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../constants";
|
||||
import type { EnergyViewStrategyConfig } from "./energy-cards";
|
||||
import { isEnergyCardHidden } from "./energy-cards";
|
||||
import { shouldShowFloorsAndAreas } from "./show-floors-and-areas";
|
||||
import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
|
||||
import type { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
|
||||
@@ -16,12 +15,11 @@ export class PowerViewStrategy extends ReactiveElement {
|
||||
static registryDependencies: readonly LovelaceStrategyDependency[] = [];
|
||||
|
||||
static async generate(
|
||||
_config: EnergyViewStrategyConfig,
|
||||
_config: LovelaceStrategyConfig,
|
||||
hass: HomeAssistant
|
||||
): Promise<LovelaceViewConfig> {
|
||||
const collectionKey =
|
||||
_config.collection_key || DEFAULT_ENERGY_COLLECTION_KEY;
|
||||
const hidden = _config.hidden_cards;
|
||||
|
||||
const energyCollection = getEnergyDataCollection(hass, {
|
||||
key: collectionKey,
|
||||
@@ -81,18 +79,14 @@ export class PowerViewStrategy extends ReactiveElement {
|
||||
collection_key: collectionKey,
|
||||
});
|
||||
|
||||
if (!isEnergyCardHidden("now", "power-sources-graph", hidden)) {
|
||||
chartsSection.cards!.push({
|
||||
title: hass.localize(
|
||||
"ui.panel.energy.cards.power_sources_graph_title"
|
||||
),
|
||||
type: "power-sources-graph",
|
||||
collection_key: collectionKey,
|
||||
grid_options: {
|
||||
columns: 36,
|
||||
},
|
||||
});
|
||||
}
|
||||
chartsSection.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.power_sources_graph_title"),
|
||||
type: "power-sources-graph",
|
||||
collection_key: collectionKey,
|
||||
grid_options: {
|
||||
columns: 36,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (hasGasSources) {
|
||||
@@ -118,7 +112,7 @@ export class PowerViewStrategy extends ReactiveElement {
|
||||
}
|
||||
});
|
||||
|
||||
if (hasPowerDevices && !isEnergyCardHidden("now", "power-sankey", hidden)) {
|
||||
if (hasPowerDevices) {
|
||||
const showFloorsAndAreas = shouldShowFloorsAndAreas(
|
||||
prefs.device_consumption,
|
||||
hass,
|
||||
@@ -136,10 +130,7 @@ export class PowerViewStrategy extends ReactiveElement {
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
hasWaterDevices &&
|
||||
!isEnergyCardHidden("now", "water-flow-sankey", hidden)
|
||||
) {
|
||||
if (hasWaterDevices) {
|
||||
const showFloorsAndAreas = shouldShowFloorsAndAreas(
|
||||
prefs.device_consumption_water,
|
||||
hass,
|
||||
|
||||
@@ -2,12 +2,11 @@ import { ReactiveElement } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { getEnergyDataCollection } from "../../../data/energy";
|
||||
import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceStrategyDependency } from "../../lovelace/strategies/types";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../constants";
|
||||
import type { EnergyViewStrategyConfig } from "./energy-cards";
|
||||
import { isEnergyCardHidden } from "./energy-cards";
|
||||
import { shouldShowFloorsAndAreas } from "./show-floors-and-areas";
|
||||
|
||||
@customElement("water-view-strategy")
|
||||
@@ -15,12 +14,11 @@ export class WaterViewStrategy extends ReactiveElement {
|
||||
static registryDependencies: readonly LovelaceStrategyDependency[] = [];
|
||||
|
||||
static async generate(
|
||||
_config: EnergyViewStrategyConfig,
|
||||
_config: LovelaceStrategyConfig,
|
||||
hass: HomeAssistant
|
||||
): Promise<LovelaceViewConfig> {
|
||||
const collectionKey =
|
||||
_config.collection_key || DEFAULT_ENERGY_COLLECTION_KEY;
|
||||
const hidden = _config.hidden_cards;
|
||||
|
||||
const view: LovelaceViewConfig = {
|
||||
type: "sections",
|
||||
@@ -65,38 +63,29 @@ export class WaterViewStrategy extends ReactiveElement {
|
||||
});
|
||||
|
||||
if (hasWaterSources) {
|
||||
if (!isEnergyCardHidden("water", "energy-water-graph", hidden)) {
|
||||
section.cards!.push({
|
||||
title: hass.localize(
|
||||
"ui.panel.energy.cards.energy_water_graph_title"
|
||||
),
|
||||
type: "energy-water-graph",
|
||||
collection_key: collectionKey,
|
||||
grid_options: {
|
||||
columns: 24,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (!isEnergyCardHidden("water", "energy-sources-table", hidden)) {
|
||||
section.cards!.push({
|
||||
title: hass.localize(
|
||||
"ui.panel.energy.cards.energy_sources_table_title"
|
||||
),
|
||||
type: "energy-sources-table",
|
||||
collection_key: collectionKey,
|
||||
types: ["water"],
|
||||
grid_options: {
|
||||
columns: 12,
|
||||
},
|
||||
});
|
||||
}
|
||||
section.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.energy_water_graph_title"),
|
||||
type: "energy-water-graph",
|
||||
collection_key: collectionKey,
|
||||
grid_options: {
|
||||
columns: 24,
|
||||
},
|
||||
});
|
||||
section.cards!.push({
|
||||
title: hass.localize(
|
||||
"ui.panel.energy.cards.energy_sources_table_title"
|
||||
),
|
||||
type: "energy-sources-table",
|
||||
collection_key: collectionKey,
|
||||
types: ["water"],
|
||||
grid_options: {
|
||||
columns: 12,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Only include if we have at least 1 water device in the config.
|
||||
if (
|
||||
hasWaterDevices &&
|
||||
!isEnergyCardHidden("water", "water-sankey", hidden)
|
||||
) {
|
||||
if (hasWaterDevices) {
|
||||
const showFloorsAndAreas = shouldShowFloorsAndAreas(
|
||||
prefs.device_consumption_water,
|
||||
hass,
|
||||
|
||||
@@ -129,7 +129,13 @@ class HaPanelHistory extends LitElement {
|
||||
@click=${this._goBack}
|
||||
></ha-icon-button-arrow-prev>
|
||||
`
|
||||
: html`<ha-menu-button slot="navigationIcon"></ha-menu-button>`}
|
||||
: html`
|
||||
<ha-menu-button
|
||||
slot="navigationIcon"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
`}
|
||||
<h1 class="page-title" slot="title">
|
||||
${this.hass.localize("panel.history")}
|
||||
</h1>
|
||||
|
||||
@@ -105,7 +105,13 @@ class PanelLight extends LitElement {
|
||||
slot="navigationIcon"
|
||||
></ha-icon-button-arrow-prev>
|
||||
`
|
||||
: html`<ha-menu-button slot="navigationIcon"></ha-menu-button>`}
|
||||
: html`
|
||||
<ha-menu-button
|
||||
slot="navigationIcon"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
`}
|
||||
<div slot="title">${this.hass.localize("panel.light")}</div>
|
||||
${this._lovelace
|
||||
? html`
|
||||
|
||||
@@ -76,7 +76,13 @@ export class HaPanelLogbook extends LitElement {
|
||||
@click=${this._goBack}
|
||||
></ha-icon-button-arrow-prev>
|
||||
`
|
||||
: html`<ha-menu-button slot="navigationIcon"></ha-menu-button>`}
|
||||
: html`
|
||||
<ha-menu-button
|
||||
slot="navigationIcon"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
`}
|
||||
<div slot="title">${this.hass.localize("panel.logbook")}</div>
|
||||
<ha-icon-button
|
||||
slot="actionItems"
|
||||
|
||||
@@ -181,6 +181,7 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
|
||||
? stateObj.attributes[this._config.attribute!] !== undefined
|
||||
? html`<ha-attribute-value
|
||||
hide-unit
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
.attribute=${this._config.attribute!}
|
||||
>
|
||||
|
||||
@@ -15,7 +15,6 @@ import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { getEntityLocation } from "../../../common/entity/get_entity_location";
|
||||
import { deepEqual } from "../../../common/util/deep-equal";
|
||||
import parseAspectRatio from "../../../common/util/parse-aspect-ratio";
|
||||
import "../../../components/ha-alert";
|
||||
@@ -91,7 +90,10 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
||||
const personSources = new Set<string>();
|
||||
const locationEntities: string[] = [];
|
||||
Object.values(hass.states).forEach((entity) => {
|
||||
if (!getEntityLocation(entity, hass.states)) {
|
||||
if (
|
||||
!("latitude" in entity.attributes) ||
|
||||
!("longitude" in entity.attributes)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
locationEntities.push(entity.entity_id);
|
||||
|
||||
@@ -133,6 +133,7 @@ export class HuiCardLayoutEditor extends LitElement {
|
||||
"max-width": `${(this.sectionConfig.column_span ?? 1) * 250 + 40}px`,
|
||||
})}
|
||||
.columns=${gridTotalColumns}
|
||||
.hass=${this.hass}
|
||||
.value=${gridValue}
|
||||
.isDefault=${this._isDefault(configOptions)}
|
||||
@value-changed=${this._gridSizeChanged}
|
||||
|
||||
@@ -16,11 +16,10 @@ import {
|
||||
union,
|
||||
} from "superstruct";
|
||||
import { ContextProvider } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||
import { getEntityLocation } from "../../../../common/entity/get_entity_location";
|
||||
import { hasLocation } from "../../../../common/entity/has_location";
|
||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
import { orderProperties } from "../../../../common/util/order-properties";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
@@ -276,13 +275,10 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
||||
this._locationEntities = !this.hass
|
||||
? []
|
||||
: Object.keys(this.hass!.states).filter((entity_id) =>
|
||||
getEntityLocation(this.hass!.states[entity_id], this.hass!.states)
|
||||
hasLocation(this.hass!.states[entity_id])
|
||||
);
|
||||
}
|
||||
|
||||
private _entityHasLocation = (stateObj: HassEntity) =>
|
||||
!!getEntityLocation(stateObj, this.hass!.states);
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
@@ -324,7 +320,7 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
||||
<hui-entity-editor
|
||||
.hass=${this.hass}
|
||||
.entities=${configEntities}
|
||||
.entityFilter=${this._entityHasLocation}
|
||||
.entityFilter=${hasLocation}
|
||||
can-edit
|
||||
.required=${!this._config.show_all}
|
||||
@entities-changed=${this._entitiesValueChanged}
|
||||
|
||||
+35
-39
@@ -11,11 +11,6 @@ import type {
|
||||
MediaPlayerSoundModeCardFeatureConfig,
|
||||
} from "../../card-features/types";
|
||||
import type { LovelaceCardFeatureEditor } from "../../types";
|
||||
import {
|
||||
customizableListData,
|
||||
customizableListSchema,
|
||||
processCustomizableListValue,
|
||||
} from "./customizable-list-feature";
|
||||
|
||||
@customElement("hui-media-player-sound-mode-card-feature-editor")
|
||||
export class HuiMediaPlayerSoundModeCardFeatureEditor
|
||||
@@ -33,20 +28,28 @@ export class HuiMediaPlayerSoundModeCardFeatureEditor
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(stateObj: MediaPlayerEntity | undefined, customize: boolean) =>
|
||||
customizableListSchema({
|
||||
field: "sound_modes",
|
||||
customize,
|
||||
options:
|
||||
stateObj?.attributes.sound_mode_list?.map((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"sound_mode",
|
||||
mode
|
||||
),
|
||||
})) ?? [],
|
||||
})
|
||||
(hass: HomeAssistant, stateObj?: MediaPlayerEntity) =>
|
||||
[
|
||||
{
|
||||
name: "sound_modes",
|
||||
selector: {
|
||||
select: {
|
||||
multiple: true,
|
||||
mode: "list" as const,
|
||||
reorder: true,
|
||||
options:
|
||||
stateObj?.attributes.sound_mode_list?.map((mode) => ({
|
||||
value: mode,
|
||||
label: hass.formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"sound_mode",
|
||||
mode
|
||||
),
|
||||
})) ?? [],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as const
|
||||
);
|
||||
|
||||
protected render() {
|
||||
@@ -60,13 +63,12 @@ export class HuiMediaPlayerSoundModeCardFeatureEditor
|
||||
| undefined)
|
||||
: undefined;
|
||||
|
||||
const data = customizableListData(this._config, "sound_modes");
|
||||
const schema = this._schema(stateObj, data.customize);
|
||||
const schema = this._schema(this.hass!, stateObj);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.data=${this._config}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
@@ -77,27 +79,21 @@ export class HuiMediaPlayerSoundModeCardFeatureEditor
|
||||
private _valueChanged(
|
||||
ev: ValueChangedEvent<MediaPlayerSoundModeCardFeatureConfig>
|
||||
): void {
|
||||
const stateObj = this.context?.entity_id
|
||||
? (this.hass!.states[this.context.entity_id] as
|
||||
| MediaPlayerEntity
|
||||
| undefined)
|
||||
: undefined;
|
||||
const defaults = stateObj?.attributes.sound_mode_list ?? [];
|
||||
const config =
|
||||
processCustomizableListValue<MediaPlayerSoundModeCardFeatureConfig>(
|
||||
ev.detail.value,
|
||||
"sound_modes",
|
||||
defaults
|
||||
);
|
||||
fireEvent(this, "config-changed", { config });
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
) =>
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.features.types.media-player-sound-mode.${schema.name}`
|
||||
);
|
||||
) => {
|
||||
switch (schema.name) {
|
||||
case "sound_modes":
|
||||
return this.hass?.localize(
|
||||
`ui.panel.lovelace.editor.features.types.media-player-sound-mode.${schema.name}`
|
||||
);
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
+31
-39
@@ -11,11 +11,6 @@ import type {
|
||||
MediaPlayerSourceCardFeatureConfig,
|
||||
} from "../../card-features/types";
|
||||
import type { LovelaceCardFeatureEditor } from "../../types";
|
||||
import {
|
||||
customizableListData,
|
||||
customizableListSchema,
|
||||
processCustomizableListValue,
|
||||
} from "./customizable-list-feature";
|
||||
|
||||
@customElement("hui-media-player-source-card-feature-editor")
|
||||
export class HuiMediaPlayerSourceCardFeatureEditor
|
||||
@@ -33,20 +28,24 @@ export class HuiMediaPlayerSourceCardFeatureEditor
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(stateObj: MediaPlayerEntity | undefined, customize: boolean) =>
|
||||
customizableListSchema({
|
||||
field: "sources",
|
||||
customize,
|
||||
options:
|
||||
stateObj?.attributes.source_list?.map((source) => ({
|
||||
value: source,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"source",
|
||||
source
|
||||
),
|
||||
})) ?? [],
|
||||
})
|
||||
(stateObj?: MediaPlayerEntity) =>
|
||||
[
|
||||
{
|
||||
name: "sources",
|
||||
selector: {
|
||||
select: {
|
||||
multiple: true,
|
||||
mode: "list" as const,
|
||||
reorder: true,
|
||||
options:
|
||||
stateObj?.attributes.source_list?.map((source) => ({
|
||||
value: source,
|
||||
label: source,
|
||||
})) ?? [],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as const
|
||||
);
|
||||
|
||||
protected render() {
|
||||
@@ -60,13 +59,12 @@ export class HuiMediaPlayerSourceCardFeatureEditor
|
||||
| undefined)
|
||||
: undefined;
|
||||
|
||||
const data = customizableListData(this._config, "sources");
|
||||
const schema = this._schema(stateObj, data.customize);
|
||||
const schema = this._schema(stateObj);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.data=${this._config}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
@@ -77,27 +75,21 @@ export class HuiMediaPlayerSourceCardFeatureEditor
|
||||
private _valueChanged(
|
||||
ev: ValueChangedEvent<MediaPlayerSourceCardFeatureConfig>
|
||||
): void {
|
||||
const stateObj = this.context?.entity_id
|
||||
? (this.hass!.states[this.context.entity_id] as
|
||||
| MediaPlayerEntity
|
||||
| undefined)
|
||||
: undefined;
|
||||
const defaults = stateObj?.attributes.source_list ?? [];
|
||||
const config =
|
||||
processCustomizableListValue<MediaPlayerSourceCardFeatureConfig>(
|
||||
ev.detail.value,
|
||||
"sources",
|
||||
defaults
|
||||
);
|
||||
fireEvent(this, "config-changed", { config });
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
) =>
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.features.types.media-player-source.${schema.name}`
|
||||
);
|
||||
) => {
|
||||
switch (schema.name) {
|
||||
case "sources":
|
||||
return this.hass?.localize(
|
||||
`ui.panel.lovelace.editor.features.types.media-player-source.${schema.name}`
|
||||
);
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -43,7 +43,8 @@ class HuiClimateEntityRow extends LitElement implements LovelaceRow {
|
||||
|
||||
return html`
|
||||
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
|
||||
<ha-climate-state .stateObj=${stateObj}> </ha-climate-state>
|
||||
<ha-climate-state .hass=${this.hass} .stateObj=${stateObj}>
|
||||
</ha-climate-state>
|
||||
</hui-generic-entity-row>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -48,11 +48,15 @@ class HuiCoverEntityRow extends LitElement implements LovelaceRow {
|
||||
${isTiltOnly(stateObj)
|
||||
? html`
|
||||
<ha-cover-tilt-controls
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
></ha-cover-tilt-controls>
|
||||
`
|
||||
: html`
|
||||
<ha-cover-controls .stateObj=${stateObj}></ha-cover-controls>
|
||||
<ha-cover-controls
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
></ha-cover-controls>
|
||||
`}
|
||||
</hui-generic-entity-row>
|
||||
`;
|
||||
|
||||
@@ -45,7 +45,8 @@ class HuiHumidifierEntityRow extends LitElement implements LovelaceRow {
|
||||
|
||||
return html`
|
||||
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
|
||||
<ha-humidifier-state .stateObj=${stateObj}> </ha-humidifier-state>
|
||||
<ha-humidifier-state .hass=${this.hass} .stateObj=${stateObj}>
|
||||
</ha-humidifier-state>
|
||||
</hui-generic-entity-row>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -136,10 +136,7 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
}
|
||||
|
||||
ha-yaml-editor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.save-button {
|
||||
|
||||
@@ -522,6 +522,7 @@ class HUIRoot extends LitElement {
|
||||
@click=${this._editView}
|
||||
></ha-icon-button>
|
||||
<ha-icon-button-arrow-next
|
||||
.hass=${this.hass}
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.edit_view.move_right"
|
||||
)}
|
||||
@@ -577,6 +578,8 @@ class HUIRoot extends LitElement {
|
||||
: html`
|
||||
<ha-menu-button
|
||||
slot="navigationIcon"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
`}
|
||||
${isSubview
|
||||
|
||||
@@ -69,6 +69,7 @@ class HuiAttributeRow extends LitElement implements LovelaceRow {
|
||||
<ha-attribute-value
|
||||
.hideUnit=${this._config.suffix !== undefined &&
|
||||
this._config.suffix !== ""}
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
.attribute=${this._config.attribute}
|
||||
>
|
||||
|
||||
@@ -106,7 +106,13 @@ class PanelMaintenance extends LitElement {
|
||||
slot="navigationIcon"
|
||||
></ha-icon-button-arrow-prev>
|
||||
`
|
||||
: html`<ha-menu-button slot="navigationIcon"></ha-menu-button>`}
|
||||
: html`
|
||||
<ha-menu-button
|
||||
slot="navigationIcon"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
`}
|
||||
<div class="main-title">
|
||||
${this.hass.localize("panel.maintenance")}
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,6 @@ import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
||||
import { getEntityLocation } from "../../common/entity/get_entity_location";
|
||||
import { navigate } from "../../common/navigate";
|
||||
import "../../components/ha-icon-button";
|
||||
import "../../components/ha-menu-button";
|
||||
@@ -23,7 +22,11 @@ class HaPanelMap extends LitElement {
|
||||
protected render() {
|
||||
return html`
|
||||
<ha-top-app-bar-fixed .narrow=${this.narrow}>
|
||||
<ha-menu-button slot="navigationIcon"></ha-menu-button>
|
||||
<ha-menu-button
|
||||
slot="navigationIcon"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
<div slot="title">${this.hass.localize("panel.map")}</div>
|
||||
${!__DEMO__ && this.hass.user?.is_admin
|
||||
? html`<ha-icon-button
|
||||
@@ -61,10 +64,11 @@ class HaPanelMap extends LitElement {
|
||||
const personSources = new Set<string>();
|
||||
const locationEntities: string[] = [];
|
||||
Object.values(this.hass!.states).forEach((entity) => {
|
||||
if (entity.state === "home") {
|
||||
return;
|
||||
}
|
||||
if (!getEntityLocation(entity, this.hass!.states)) {
|
||||
if (
|
||||
entity.state === "home" ||
|
||||
!("latitude" in entity.attributes) ||
|
||||
!("longitude" in entity.attributes)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
locationEntities.push(entity.entity_id);
|
||||
|
||||
@@ -100,7 +100,13 @@ class PanelMediaBrowser extends LitElement {
|
||||
@click=${this._goBack}
|
||||
></ha-icon-button-arrow-prev>
|
||||
`
|
||||
: html`<ha-menu-button slot="navigationIcon"></ha-menu-button>`}
|
||||
: html`
|
||||
<ha-menu-button
|
||||
slot="navigationIcon"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
`}
|
||||
<h1 class="page-title" slot="title">
|
||||
${!this._currentItem
|
||||
? this.hass.localize(
|
||||
|
||||
@@ -296,9 +296,6 @@ export const getMyRedirects = (): Redirects => ({
|
||||
component: "history",
|
||||
redirect: "/history",
|
||||
},
|
||||
maintenance: {
|
||||
redirect: "/maintenance",
|
||||
},
|
||||
overview: {
|
||||
redirect: "/home/overview",
|
||||
},
|
||||
|
||||
@@ -106,7 +106,13 @@ class PanelSecurity extends LitElement {
|
||||
slot="navigationIcon"
|
||||
></ha-icon-button-arrow-prev>
|
||||
`
|
||||
: html`<ha-menu-button slot="navigationIcon"></ha-menu-button>`}
|
||||
: html`
|
||||
<ha-menu-button
|
||||
slot="navigationIcon"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
`}
|
||||
<div class="main-title">${this.hass.localize("panel.security")}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -184,7 +184,11 @@ class PanelTodo extends LitElement {
|
||||
footer
|
||||
.narrow=${this.narrow}
|
||||
>
|
||||
<ha-menu-button slot="navigationIcon"></ha-menu-button>
|
||||
<ha-menu-button
|
||||
slot="navigationIcon"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
<div slot="title">
|
||||
${!showPane
|
||||
? html`<ha-dropdown class="lists">
|
||||
|
||||
@@ -23,7 +23,10 @@ class StateCardClimate extends LitElement {
|
||||
.stateObj=${this.stateObj}
|
||||
.inDialog=${this.inDialog}
|
||||
></state-info>
|
||||
<ha-climate-state .stateObj=${this.stateObj}></ha-climate-state>
|
||||
<ha-climate-state
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
></ha-climate-state>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -26,10 +26,12 @@ class StateCardCover extends LitElement {
|
||||
.inDialog=${this.inDialog}
|
||||
></state-info>
|
||||
<ha-cover-controls
|
||||
.hass=${this.hass}
|
||||
.hidden=${isTiltOnly(this.stateObj)}
|
||||
.stateObj=${this.stateObj}
|
||||
></ha-cover-controls>
|
||||
<ha-cover-tilt-controls
|
||||
.hass=${this.hass}
|
||||
.hidden=${!isTiltOnly(this.stateObj)}
|
||||
.stateObj=${this.stateObj}
|
||||
></ha-cover-tilt-controls>
|
||||
|
||||
@@ -25,7 +25,10 @@ class StateCardHumidifier extends LitElement {
|
||||
.inDialog=${this.inDialog}
|
||||
>
|
||||
</state-info>
|
||||
<ha-humidifier-state .stateObj=${this.stateObj}></ha-humidifier-state>
|
||||
<ha-humidifier-state
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
></ha-humidifier-state>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3832,7 +3832,6 @@
|
||||
"developer-tools": {
|
||||
"tabs": {
|
||||
"assist": {
|
||||
"tab": "Assist",
|
||||
"title": "Sentences parser",
|
||||
"description": "Enter sentences and see how they will be parsed by Home Assistant. Each line will be processed as an individual sentence. Intents will not be executed on your instance.",
|
||||
"parse_sentences": "Parse sentences",
|
||||
@@ -4140,17 +4139,6 @@
|
||||
"gas": "Gas",
|
||||
"water": "Water"
|
||||
},
|
||||
"customise": {
|
||||
"toolbar_action": "Customize cards",
|
||||
"title": "Customize energy",
|
||||
"saved": "Energy dashboard updated",
|
||||
"save_failed": "Failed to save energy customization",
|
||||
"unavailable": "This card isn't shown because the energy source or device it needs isn't configured.",
|
||||
"groups": {
|
||||
"overview": "Overview",
|
||||
"now": "Now"
|
||||
}
|
||||
},
|
||||
"delete_source": "Are you sure you want to remove this source?",
|
||||
"delete_integration": "Are you sure you want to remove this integration? It will remove the entities it provides",
|
||||
"grid": {
|
||||
@@ -10161,13 +10149,11 @@
|
||||
},
|
||||
"media-player-sound-mode": {
|
||||
"label": "Media player sound mode",
|
||||
"sound_modes": "Sound modes",
|
||||
"customize": "Customize sound modes"
|
||||
"sound_modes": "Sound modes"
|
||||
},
|
||||
"media-player-source": {
|
||||
"label": "Media player source",
|
||||
"sources": "Sources",
|
||||
"customize": "Customize sources"
|
||||
"sources": "Sources"
|
||||
},
|
||||
"media-player-volume-buttons": {
|
||||
"label": "Media player volume buttons",
|
||||
@@ -11208,12 +11194,7 @@
|
||||
"energy_top_consumers_title": "Top consumers",
|
||||
"power_sankey_title": "Current power flow",
|
||||
"water_flow_sankey_title": "Current water flow",
|
||||
"power_sources_graph_title": "Power sources",
|
||||
"energy_grid_balance_title": "Grid energy balance",
|
||||
"energy_grid_neutrality_gauge_title": "Grid neutrality gauge",
|
||||
"energy_solar_consumed_gauge_title": "Solar consumed gauge",
|
||||
"energy_self_sufficiency_gauge_title": "Self-sufficiency gauge",
|
||||
"energy_carbon_consumed_gauge_title": "Carbon consumed gauge"
|
||||
"power_sources_graph_title": "Power sources"
|
||||
}
|
||||
},
|
||||
"history": {
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type {
|
||||
EnergyPreferences,
|
||||
EnergySource,
|
||||
} from "../../../../src/data/energy";
|
||||
import {
|
||||
applicableEnergyCardKeys,
|
||||
ENERGY_CARD_CATALOG,
|
||||
energyCardKey,
|
||||
isEnergyCardHidden,
|
||||
isEnergyViewEmpty,
|
||||
} from "../../../../src/panels/energy/strategies/energy-cards";
|
||||
|
||||
const source = (s: Partial<EnergySource> & { type: string }): EnergySource =>
|
||||
s as unknown as EnergySource;
|
||||
|
||||
const makePrefs = (
|
||||
prefs: Partial<EnergyPreferences> = {}
|
||||
): EnergyPreferences => ({
|
||||
energy_sources: [],
|
||||
device_consumption: [],
|
||||
device_consumption_water: [],
|
||||
...prefs,
|
||||
});
|
||||
|
||||
const GRID_RETURN = source({
|
||||
type: "grid",
|
||||
stat_energy_from: "sensor.grid_in",
|
||||
stat_energy_to: "sensor.grid_out",
|
||||
});
|
||||
const SOLAR = source({ type: "solar", stat_energy_from: "sensor.solar" });
|
||||
const GAS = source({ type: "gas", stat_energy_from: "sensor.gas" });
|
||||
const WATER = source({ type: "water", stat_energy_from: "sensor.water" });
|
||||
|
||||
describe("energyCardKey", () => {
|
||||
it("joins the view path and card type", () => {
|
||||
expect(energyCardKey("electricity", "energy-solar-graph")).toBe(
|
||||
"electricity.energy-solar-graph"
|
||||
);
|
||||
expect(energyCardKey("now", "power-sankey")).toBe("now.power-sankey");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isEnergyCardHidden", () => {
|
||||
it("returns true only when the composite key is in the hidden list", () => {
|
||||
const hidden = ["electricity.energy-solar-graph"];
|
||||
expect(
|
||||
isEnergyCardHidden("electricity", "energy-solar-graph", hidden)
|
||||
).toBe(true);
|
||||
// Same card type in a different view is independent.
|
||||
expect(isEnergyCardHidden("overview", "energy-solar-graph", hidden)).toBe(
|
||||
false
|
||||
);
|
||||
expect(
|
||||
isEnergyCardHidden("electricity", "energy-usage-graph", hidden)
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("treats undefined/empty hidden lists as nothing hidden", () => {
|
||||
expect(
|
||||
isEnergyCardHidden("electricity", "energy-solar-graph", undefined)
|
||||
).toBe(false);
|
||||
expect(isEnergyCardHidden("electricity", "energy-solar-graph", [])).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("catalog applicability", () => {
|
||||
it("only lists cards relevant to the configured sources", () => {
|
||||
const gasOnly = makePrefs({ energy_sources: [GAS] });
|
||||
expect(applicableEnergyCardKeys("gas", gasOnly)).toEqual([
|
||||
"gas.energy-gas-graph",
|
||||
"gas.energy-sources-table",
|
||||
]);
|
||||
// No electricity sources -> no electricity cards apply.
|
||||
expect(applicableEnergyCardKeys("electricity", gasOnly)).toEqual([]);
|
||||
});
|
||||
|
||||
it("gates the solar graph and gauges on their sources", () => {
|
||||
const solarGraph = ENERGY_CARD_CATALOG.find(
|
||||
(c) => c.key === "electricity.energy-solar-graph"
|
||||
)!;
|
||||
expect(
|
||||
solarGraph.isApplicable(makePrefs({ energy_sources: [SOLAR] }))
|
||||
).toBe(true);
|
||||
expect(
|
||||
solarGraph.isApplicable(makePrefs({ energy_sources: [GRID_RETURN] }))
|
||||
).toBe(false);
|
||||
|
||||
const neutralityGauge = ENERGY_CARD_CATALOG.find(
|
||||
(c) => c.key === "electricity.energy-grid-neutrality-gauge"
|
||||
)!;
|
||||
// Needs grid export (return).
|
||||
expect(
|
||||
neutralityGauge.isApplicable(makePrefs({ energy_sources: [GRID_RETURN] }))
|
||||
).toBe(true);
|
||||
expect(
|
||||
neutralityGauge.isApplicable(makePrefs({ energy_sources: [SOLAR] }))
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isEnergyViewEmpty", () => {
|
||||
const prefs = makePrefs({ energy_sources: [WATER] });
|
||||
|
||||
it("is false when no cards in the view are hidden", () => {
|
||||
expect(isEnergyViewEmpty("water", prefs, undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it("is false when only some applicable cards are hidden", () => {
|
||||
expect(
|
||||
isEnergyViewEmpty("water", prefs, ["water.energy-water-graph"])
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("is true when every applicable card is hidden", () => {
|
||||
expect(
|
||||
isEnergyViewEmpty("water", prefs, [
|
||||
"water.energy-water-graph",
|
||||
"water.energy-sources-table",
|
||||
])
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("is false when the view has no applicable cards at all", () => {
|
||||
// Water source configured, but the gas view has nothing applicable.
|
||||
expect(isEnergyViewEmpty("gas", prefs, [])).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -3591,51 +3591,51 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rspack/binding-darwin-arm64@npm:2.0.6":
|
||||
version: 2.0.6
|
||||
resolution: "@rspack/binding-darwin-arm64@npm:2.0.6"
|
||||
"@rspack/binding-darwin-arm64@npm:2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "@rspack/binding-darwin-arm64@npm:2.0.5"
|
||||
conditions: os=darwin & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rspack/binding-darwin-x64@npm:2.0.6":
|
||||
version: 2.0.6
|
||||
resolution: "@rspack/binding-darwin-x64@npm:2.0.6"
|
||||
"@rspack/binding-darwin-x64@npm:2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "@rspack/binding-darwin-x64@npm:2.0.5"
|
||||
conditions: os=darwin & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rspack/binding-linux-arm64-gnu@npm:2.0.6":
|
||||
version: 2.0.6
|
||||
resolution: "@rspack/binding-linux-arm64-gnu@npm:2.0.6"
|
||||
"@rspack/binding-linux-arm64-gnu@npm:2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "@rspack/binding-linux-arm64-gnu@npm:2.0.5"
|
||||
conditions: os=linux & cpu=arm64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rspack/binding-linux-arm64-musl@npm:2.0.6":
|
||||
version: 2.0.6
|
||||
resolution: "@rspack/binding-linux-arm64-musl@npm:2.0.6"
|
||||
"@rspack/binding-linux-arm64-musl@npm:2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "@rspack/binding-linux-arm64-musl@npm:2.0.5"
|
||||
conditions: os=linux & cpu=arm64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rspack/binding-linux-x64-gnu@npm:2.0.6":
|
||||
version: 2.0.6
|
||||
resolution: "@rspack/binding-linux-x64-gnu@npm:2.0.6"
|
||||
"@rspack/binding-linux-x64-gnu@npm:2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "@rspack/binding-linux-x64-gnu@npm:2.0.5"
|
||||
conditions: os=linux & cpu=x64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rspack/binding-linux-x64-musl@npm:2.0.6":
|
||||
version: 2.0.6
|
||||
resolution: "@rspack/binding-linux-x64-musl@npm:2.0.6"
|
||||
"@rspack/binding-linux-x64-musl@npm:2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "@rspack/binding-linux-x64-musl@npm:2.0.5"
|
||||
conditions: os=linux & cpu=x64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rspack/binding-wasm32-wasi@npm:2.0.6":
|
||||
version: 2.0.6
|
||||
resolution: "@rspack/binding-wasm32-wasi@npm:2.0.6"
|
||||
"@rspack/binding-wasm32-wasi@npm:2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "@rspack/binding-wasm32-wasi@npm:2.0.5"
|
||||
dependencies:
|
||||
"@emnapi/core": "npm:1.10.0"
|
||||
"@emnapi/runtime": "npm:1.10.0"
|
||||
@@ -3644,41 +3644,41 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rspack/binding-win32-arm64-msvc@npm:2.0.6":
|
||||
version: 2.0.6
|
||||
resolution: "@rspack/binding-win32-arm64-msvc@npm:2.0.6"
|
||||
"@rspack/binding-win32-arm64-msvc@npm:2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "@rspack/binding-win32-arm64-msvc@npm:2.0.5"
|
||||
conditions: os=win32 & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rspack/binding-win32-ia32-msvc@npm:2.0.6":
|
||||
version: 2.0.6
|
||||
resolution: "@rspack/binding-win32-ia32-msvc@npm:2.0.6"
|
||||
"@rspack/binding-win32-ia32-msvc@npm:2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "@rspack/binding-win32-ia32-msvc@npm:2.0.5"
|
||||
conditions: os=win32 & cpu=ia32
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rspack/binding-win32-x64-msvc@npm:2.0.6":
|
||||
version: 2.0.6
|
||||
resolution: "@rspack/binding-win32-x64-msvc@npm:2.0.6"
|
||||
"@rspack/binding-win32-x64-msvc@npm:2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "@rspack/binding-win32-x64-msvc@npm:2.0.5"
|
||||
conditions: os=win32 & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rspack/binding@npm:2.0.6":
|
||||
version: 2.0.6
|
||||
resolution: "@rspack/binding@npm:2.0.6"
|
||||
"@rspack/binding@npm:2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "@rspack/binding@npm:2.0.5"
|
||||
dependencies:
|
||||
"@rspack/binding-darwin-arm64": "npm:2.0.6"
|
||||
"@rspack/binding-darwin-x64": "npm:2.0.6"
|
||||
"@rspack/binding-linux-arm64-gnu": "npm:2.0.6"
|
||||
"@rspack/binding-linux-arm64-musl": "npm:2.0.6"
|
||||
"@rspack/binding-linux-x64-gnu": "npm:2.0.6"
|
||||
"@rspack/binding-linux-x64-musl": "npm:2.0.6"
|
||||
"@rspack/binding-wasm32-wasi": "npm:2.0.6"
|
||||
"@rspack/binding-win32-arm64-msvc": "npm:2.0.6"
|
||||
"@rspack/binding-win32-ia32-msvc": "npm:2.0.6"
|
||||
"@rspack/binding-win32-x64-msvc": "npm:2.0.6"
|
||||
"@rspack/binding-darwin-arm64": "npm:2.0.5"
|
||||
"@rspack/binding-darwin-x64": "npm:2.0.5"
|
||||
"@rspack/binding-linux-arm64-gnu": "npm:2.0.5"
|
||||
"@rspack/binding-linux-arm64-musl": "npm:2.0.5"
|
||||
"@rspack/binding-linux-x64-gnu": "npm:2.0.5"
|
||||
"@rspack/binding-linux-x64-musl": "npm:2.0.5"
|
||||
"@rspack/binding-wasm32-wasi": "npm:2.0.5"
|
||||
"@rspack/binding-win32-arm64-msvc": "npm:2.0.5"
|
||||
"@rspack/binding-win32-ia32-msvc": "npm:2.0.5"
|
||||
"@rspack/binding-win32-x64-msvc": "npm:2.0.5"
|
||||
dependenciesMeta:
|
||||
"@rspack/binding-darwin-arm64":
|
||||
optional: true
|
||||
@@ -3700,15 +3700,15 @@ __metadata:
|
||||
optional: true
|
||||
"@rspack/binding-win32-x64-msvc":
|
||||
optional: true
|
||||
checksum: 10/c2e5245abab3257d02f5d98947fad26c8de1b18bb17362734035cfbdd725d9c6c78432372bdff985b32fa4062059d7210e9f5ea7314ae3080805b64f616fe348
|
||||
checksum: 10/95b4fa9daf1935e2ca3d1bd08b12c7f26ef0fffb09d7764bcb11fed1d4ecedc4dd79cbfbb37519121e81c06b97ade27f2d7a33ed0ed51e3b7d650b37b8b33c9e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rspack/core@npm:2.0.6":
|
||||
version: 2.0.6
|
||||
resolution: "@rspack/core@npm:2.0.6"
|
||||
"@rspack/core@npm:2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "@rspack/core@npm:2.0.5"
|
||||
dependencies:
|
||||
"@rspack/binding": "npm:2.0.6"
|
||||
"@rspack/binding": "npm:2.0.5"
|
||||
peerDependencies:
|
||||
"@module-federation/runtime-tools": ^0.24.1 || ^2.0.0
|
||||
"@swc/helpers": ^0.5.23
|
||||
@@ -3717,7 +3717,7 @@ __metadata:
|
||||
optional: true
|
||||
"@swc/helpers":
|
||||
optional: true
|
||||
checksum: 10/d2417690e8135342179bc9e5035e16fe827522b4c0babef029a21ff5903cd56c09b86f08924527bd7d3e66f178f1f678ce099199cac8c1a137b18c5d8892e613
|
||||
checksum: 10/b955a12bd04e1e3bef954f8a3cfd48c7302d114fee4d13dc7c6f46645bb18d377a4df7f86520bbf44519ed7bb315bd119e98768bfd7d851b1329ba9ee51ce1ac
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -4041,161 +4041,161 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/basic@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/basic@npm:4.1.2"
|
||||
"@tsparticles/basic@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/basic@npm:4.1.1"
|
||||
dependencies:
|
||||
"@tsparticles/engine": "npm:4.1.2"
|
||||
"@tsparticles/plugin-blend": "npm:4.1.2"
|
||||
"@tsparticles/plugin-hex-color": "npm:4.1.2"
|
||||
"@tsparticles/plugin-hsl-color": "npm:4.1.2"
|
||||
"@tsparticles/plugin-move": "npm:4.1.2"
|
||||
"@tsparticles/plugin-rgb-color": "npm:4.1.2"
|
||||
"@tsparticles/shape-circle": "npm:4.1.2"
|
||||
"@tsparticles/updater-opacity": "npm:4.1.2"
|
||||
"@tsparticles/updater-out-modes": "npm:4.1.2"
|
||||
"@tsparticles/updater-paint": "npm:4.1.2"
|
||||
"@tsparticles/updater-size": "npm:4.1.2"
|
||||
checksum: 10/1c145d25373562cd3b45f20664610226c050a0a6867396c2d138a76761d3e7a5796cf107d8bdcbb8eb8cca19a3a1e4192eb356d37387d6547bf6b1ff796b71b2
|
||||
"@tsparticles/engine": "npm:4.1.1"
|
||||
"@tsparticles/plugin-blend": "npm:4.1.1"
|
||||
"@tsparticles/plugin-hex-color": "npm:4.1.1"
|
||||
"@tsparticles/plugin-hsl-color": "npm:4.1.1"
|
||||
"@tsparticles/plugin-move": "npm:4.1.1"
|
||||
"@tsparticles/plugin-rgb-color": "npm:4.1.1"
|
||||
"@tsparticles/shape-circle": "npm:4.1.1"
|
||||
"@tsparticles/updater-opacity": "npm:4.1.1"
|
||||
"@tsparticles/updater-out-modes": "npm:4.1.1"
|
||||
"@tsparticles/updater-paint": "npm:4.1.1"
|
||||
"@tsparticles/updater-size": "npm:4.1.1"
|
||||
checksum: 10/99191aeee4b9a3856aa82a2cea21e54d2099b6d58b4af3bacf4bb133277bd71de16ac07fe632ebabe08cc2a06be1aa6c00d82d593027276bf588870afc9182f5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/canvas-utils@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/canvas-utils@npm:4.1.2"
|
||||
"@tsparticles/canvas-utils@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/canvas-utils@npm:4.1.1"
|
||||
peerDependencies:
|
||||
"@tsparticles/engine": 4.1.2
|
||||
checksum: 10/ffedc8400b5ff758331bdf1ef2362aac713c3e41d5bcece00153acc04c725947c41d545202d73bd6ef290d2ec220353f1801fb4ad22e99870989b0c68d7540c4
|
||||
"@tsparticles/engine": 4.1.1
|
||||
checksum: 10/2b5d7e9a55aa8086007f2dff940800c650233751ebb708fdbf9f91be4b0cd9d975400da42cdf531bb74860974dff5ea3c76199520ccd3f089904fba0d1bc0722
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/engine@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/engine@npm:4.1.2"
|
||||
checksum: 10/6fe6aa50bba564a8a8691945cb378267e695ec58323066a4157e8a036e7a3caa99e1ced08d05bffd173045bbc33c4d41523afc28624b1bea015393c1a88ef2bb
|
||||
"@tsparticles/engine@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/engine@npm:4.1.1"
|
||||
checksum: 10/74886de63046f8752515f097176cae2fa8d506fd9d2d6a84106d43a89ff688e047d99a5018e56e3fffc17d5d13a9061f63fafcb2b4ebe773f78379621d0d9855
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/interaction-particles-links@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/interaction-particles-links@npm:4.1.2"
|
||||
"@tsparticles/interaction-particles-links@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/interaction-particles-links@npm:4.1.1"
|
||||
dependencies:
|
||||
"@tsparticles/canvas-utils": "npm:4.1.2"
|
||||
"@tsparticles/canvas-utils": "npm:4.1.1"
|
||||
peerDependencies:
|
||||
"@tsparticles/engine": 4.1.2
|
||||
"@tsparticles/plugin-interactivity": 4.1.2
|
||||
checksum: 10/b9e1e85bdb3226a25d7a4486556abfe458c81c109855c04857623d2d3506ef325fcc2dc4fcee962414e216aa6346bc0849df1bf7199a3e4e75bed2fb7f069702
|
||||
"@tsparticles/engine": 4.1.1
|
||||
"@tsparticles/plugin-interactivity": 4.1.1
|
||||
checksum: 10/c0c7ad3740f2168b75ca7ae09341fefcc1d8c8d117fbee7a6519a622843dfe5d57bb65113a4eba40f26906904f069cbac196840f45fc7be12864fddfa8106184
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/plugin-blend@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/plugin-blend@npm:4.1.2"
|
||||
"@tsparticles/plugin-blend@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/plugin-blend@npm:4.1.1"
|
||||
peerDependencies:
|
||||
"@tsparticles/engine": 4.1.2
|
||||
checksum: 10/1f51bcf76d6d749a0a18799fd3a814a82e62f060fd3c20d28f8bb170311815aeba8efc361e73cef7356c51151c7debafc75d34bff6ab2d4e58099b51774a28c6
|
||||
"@tsparticles/engine": 4.1.1
|
||||
checksum: 10/9a666dc1fe0ed9ff4743fc23ca3bf27bd5d66073cc0127203b566077d431321b61af440875e96c87950e18011b1e6b9b43d328e4200923dd4be9fd934c07a5cd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/plugin-hex-color@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/plugin-hex-color@npm:4.1.2"
|
||||
"@tsparticles/plugin-hex-color@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/plugin-hex-color@npm:4.1.1"
|
||||
peerDependencies:
|
||||
"@tsparticles/engine": 4.1.2
|
||||
checksum: 10/d32a39bbd6732b9e630f84a7d226753735f02d65b70a1d5d11fa680421d5f1378aee75af1ecb5ada5af9875822bbaf2b5d1f213d1e0cbce2a50820743eb9c2fc
|
||||
"@tsparticles/engine": 4.1.1
|
||||
checksum: 10/33fde7c2763affb1dd7fa033431a2553646bde79c9ab52106be6226a9716ae973c90c8b4bf381d96f661ab75467934019cd456fe27ccac59dca11b5c989c3a75
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/plugin-hsl-color@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/plugin-hsl-color@npm:4.1.2"
|
||||
"@tsparticles/plugin-hsl-color@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/plugin-hsl-color@npm:4.1.1"
|
||||
peerDependencies:
|
||||
"@tsparticles/engine": 4.1.2
|
||||
checksum: 10/c130e80b0fd2750402fa0e309d0bb624b2138474069ae4ccb76610722c4f0966f278ad1a35c85fbcf5449914936f1c53322246f1cdd028106d83f779beee538c
|
||||
"@tsparticles/engine": 4.1.1
|
||||
checksum: 10/594f4a840f6f6f134a11d76c1a7fa80ed60a5d945534aad53e8ab5718341187f0fac73eb45d1c39246b98e982f1d60f9dbb95a083edcf8407e015048056b84e7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/plugin-interactivity@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/plugin-interactivity@npm:4.1.2"
|
||||
"@tsparticles/plugin-interactivity@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/plugin-interactivity@npm:4.1.1"
|
||||
peerDependencies:
|
||||
"@tsparticles/engine": 4.1.2
|
||||
checksum: 10/78efd4ffe3a07752f26262fe27ee0a931b45fea6c81d62e156d8f965dc22a2141d64395eb99477624ca9aac9e152f9e2585fc8aced9c822c61cb22ecfad184a4
|
||||
"@tsparticles/engine": 4.1.1
|
||||
checksum: 10/448bd8f7c741ed0359ace49a6c58d58947d95660158a131c86c248427b9caa41645c6e8c9cea6a8f0f6a70cd25c0dd64017edba79c8f225b6db07f39b660eb4d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/plugin-move@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/plugin-move@npm:4.1.2"
|
||||
"@tsparticles/plugin-move@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/plugin-move@npm:4.1.1"
|
||||
peerDependencies:
|
||||
"@tsparticles/engine": 4.1.2
|
||||
checksum: 10/fac5b90904d3fa62b310a1ce19647c5ae4a592111b5eeccfc6e946c6d17d677b0f3931ba3c114f04045827a92c1a0ed66791e99b352cb75d6c50f3749f389bfc
|
||||
"@tsparticles/engine": 4.1.1
|
||||
checksum: 10/b854cb6e2dcea2971f1abaca75c6e8cde40611178f13513559f60cc45da568e8b61c2f027619041d94e11c60af08a76eb4957d344a910b7a6255265937199094
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/plugin-rgb-color@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/plugin-rgb-color@npm:4.1.2"
|
||||
"@tsparticles/plugin-rgb-color@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/plugin-rgb-color@npm:4.1.1"
|
||||
peerDependencies:
|
||||
"@tsparticles/engine": 4.1.2
|
||||
checksum: 10/7e3e83171f74f7e9e3f68ce8d0aeb4685b277ecac3c4874a951e86228d3db78978d6197ef9e04b17190471294323c561c18c92c9d9ae8e8a115e6238cbecdb43
|
||||
"@tsparticles/engine": 4.1.1
|
||||
checksum: 10/2372dfe5ceec163b49c02b3b0169e6032fae34e4ade3c29d42bd26af23fac76b3416d70eed9cac9a0f2b470c763ee903d12d0e9156b7693b3c8908e25714cf76
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/preset-links@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/preset-links@npm:4.1.2"
|
||||
"@tsparticles/preset-links@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/preset-links@npm:4.1.1"
|
||||
dependencies:
|
||||
"@tsparticles/basic": "npm:4.1.2"
|
||||
"@tsparticles/engine": "npm:4.1.2"
|
||||
"@tsparticles/interaction-particles-links": "npm:4.1.2"
|
||||
"@tsparticles/plugin-interactivity": "npm:4.1.2"
|
||||
checksum: 10/d907884c4e9fd023aa8e0e1ff2388ef24d06a446433cc4ffbd620a4d22801f52ce97fe5f441b9fdea854f36d6f421d7571648925e5ea5c0976c121194156a2b1
|
||||
"@tsparticles/basic": "npm:4.1.1"
|
||||
"@tsparticles/engine": "npm:4.1.1"
|
||||
"@tsparticles/interaction-particles-links": "npm:4.1.1"
|
||||
"@tsparticles/plugin-interactivity": "npm:4.1.1"
|
||||
checksum: 10/26dd1dcd4ede3bae32cae3585a7f929e0089a75e389f83cb5b00213e977b647c3e7e743b5e83d3a5400beffcff9343bde7538412579708bad761d2e933708638
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/shape-circle@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/shape-circle@npm:4.1.2"
|
||||
"@tsparticles/shape-circle@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/shape-circle@npm:4.1.1"
|
||||
peerDependencies:
|
||||
"@tsparticles/engine": 4.1.2
|
||||
checksum: 10/60a87755d4e598c278c1850b58b07bcfbef603de242de3979702cd8120cf24fd95d985b2092c7a8a2cccd43eb6a1939ab215f74a93898ea1e2d425be5deb405c
|
||||
"@tsparticles/engine": 4.1.1
|
||||
checksum: 10/401e8a267cf9301dae8386e9bb1c5ff3a02dfb5b5f136187c73ed6b89e33f176f929fdc53acaa60b13f3ee1ed7f8c6f35f3f0e26ff6930a33d1949a0f23e4c1f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/updater-opacity@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/updater-opacity@npm:4.1.2"
|
||||
"@tsparticles/updater-opacity@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/updater-opacity@npm:4.1.1"
|
||||
peerDependencies:
|
||||
"@tsparticles/engine": 4.1.2
|
||||
checksum: 10/4dc426175bb3e4c5d3bc35e7d83ace7130c4f8817c7f88ee6bf1b4b1a52b28511d99b1fc7ded3c549a5baae401f7c22cc701d83919694387154aef86ea6974c2
|
||||
"@tsparticles/engine": 4.1.1
|
||||
checksum: 10/3fe157203e02dfec1ef9ac693d517cd9abcad8c28acaac8d4ca923b8301a99ae73129ed4559ea507c0d7d6ad626bdbbdb03f25868d7d28f852e2d8ffc7d13790
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/updater-out-modes@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/updater-out-modes@npm:4.1.2"
|
||||
"@tsparticles/updater-out-modes@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/updater-out-modes@npm:4.1.1"
|
||||
peerDependencies:
|
||||
"@tsparticles/engine": 4.1.2
|
||||
checksum: 10/901be7b7dfa97d3d4ff857715a6b61dd4bc2ca09f23fee38d86cd6827351134c5cd4c3fa702d2aec8601544b1f6935f2b4034c4567a00128f9cf36cbaf379722
|
||||
"@tsparticles/engine": 4.1.1
|
||||
checksum: 10/bc5f074661c42acc20a87ee3ff093fe63d2fd87a7f7d3156c2fb23dc1d24881ccc9e9cb82313ff7265317fcdc447a5b17b2204930faf8ba627278e2dc0fab2c3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/updater-paint@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/updater-paint@npm:4.1.2"
|
||||
"@tsparticles/updater-paint@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/updater-paint@npm:4.1.1"
|
||||
peerDependencies:
|
||||
"@tsparticles/engine": 4.1.2
|
||||
checksum: 10/5a0b4bc0a4c6061e1eb93a6af7a97b472e5d552677dafe8c888b0f9a71b68ed10ef255d86f2cf8d8466ecd1d33ba8f5e52920947237fe1218cbcd002c50dff47
|
||||
"@tsparticles/engine": 4.1.1
|
||||
checksum: 10/77459f6337b869d654573696dbe2bc1d238ddf3410857d8b91df606f7402301acae34f4a334727faa552f9816f518353b7a98e0bda4854ea5419cb34e8d447d2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tsparticles/updater-size@npm:4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "@tsparticles/updater-size@npm:4.1.2"
|
||||
"@tsparticles/updater-size@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@tsparticles/updater-size@npm:4.1.1"
|
||||
peerDependencies:
|
||||
"@tsparticles/engine": 4.1.2
|
||||
checksum: 10/d5c4f79d5c1d3d977dd5ebac56c9679df134ddba919b8e2ee1473820f2c551f5b162356b49b22e6e54134b87a27c51cb5a6c2e2441ba2569b0dab62974e91a47
|
||||
"@tsparticles/engine": 4.1.1
|
||||
checksum: 10/33ae9d51394e299459478a9dedd369de1bd266ad9c2e2236ec2c20bb455aad9118efa13afc7d359b6d7d010a34bba5b60cbf2e3d8b853e124c0b53c2aae8db18
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -4548,105 +4548,105 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/eslint-plugin@npm:8.60.1":
|
||||
version: 8.60.1
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.60.1"
|
||||
"@typescript-eslint/eslint-plugin@npm:8.60.0":
|
||||
version: 8.60.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.60.0"
|
||||
dependencies:
|
||||
"@eslint-community/regexpp": "npm:^4.12.2"
|
||||
"@typescript-eslint/scope-manager": "npm:8.60.1"
|
||||
"@typescript-eslint/type-utils": "npm:8.60.1"
|
||||
"@typescript-eslint/utils": "npm:8.60.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.60.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.60.0"
|
||||
"@typescript-eslint/type-utils": "npm:8.60.0"
|
||||
"@typescript-eslint/utils": "npm:8.60.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.60.0"
|
||||
ignore: "npm:^7.0.5"
|
||||
natural-compare: "npm:^1.4.0"
|
||||
ts-api-utils: "npm:^2.5.0"
|
||||
peerDependencies:
|
||||
"@typescript-eslint/parser": ^8.60.1
|
||||
"@typescript-eslint/parser": ^8.60.0
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/f3633bb2700bc32299578baeaf6650418656229be256147ba9d1ab09b34ef2b7fed83804ef4d2439e9189dbdcb89399d67bc8fea55262be6caa32114be048538
|
||||
checksum: 10/aec6f08be04ad0014c80e5cf3bd8ec83d59c44244c9ca357c4cf182b6f0debdd690e64daa88215e937183e97c4bdee6749dbf4162191c5851ae9c648439c8a96
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/parser@npm:8.60.1":
|
||||
version: 8.60.1
|
||||
resolution: "@typescript-eslint/parser@npm:8.60.1"
|
||||
"@typescript-eslint/parser@npm:8.60.0":
|
||||
version: 8.60.0
|
||||
resolution: "@typescript-eslint/parser@npm:8.60.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager": "npm:8.60.1"
|
||||
"@typescript-eslint/types": "npm:8.60.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.60.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.60.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.60.0"
|
||||
"@typescript-eslint/types": "npm:8.60.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.60.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.60.0"
|
||||
debug: "npm:^4.4.3"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/f9c484c4a3897015328f328a1c6ee778d113dd134201f635c0421cb72efe6e63f3a68524aff0df6e19e76ff93daf5cabd946e67f12f10dddcf19bda534aa68dc
|
||||
checksum: 10/f55fa3547e3d0a0ec88bcb886b9bf6cef9b425c016dfa47e2ad7fbcbaa854640ba3f501cc0115824b58f33be4bf8bdf544505847988688906d11c154b600c54d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/project-service@npm:8.60.1":
|
||||
version: 8.60.1
|
||||
resolution: "@typescript-eslint/project-service@npm:8.60.1"
|
||||
"@typescript-eslint/project-service@npm:8.60.0":
|
||||
version: 8.60.0
|
||||
resolution: "@typescript-eslint/project-service@npm:8.60.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/tsconfig-utils": "npm:^8.60.1"
|
||||
"@typescript-eslint/types": "npm:^8.60.1"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:^8.60.0"
|
||||
"@typescript-eslint/types": "npm:^8.60.0"
|
||||
debug: "npm:^4.4.3"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/fec693dd79c3a1e6a24091127a37af4eb9d9cee8192cf2a434adae48543eadff834bc0623b5b563c8b592b250bc080570f9e7b42807252ea898442c525beeee9
|
||||
checksum: 10/21e233d1292775753861aad32b30448f9fb5508f53d5a12c8ce7e75613df236757377fa877c738cc858ac863f2f8259a1f63bfb15a32ee9c5476fe9b2d12fbb0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/scope-manager@npm:8.60.1":
|
||||
version: 8.60.1
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.60.1"
|
||||
"@typescript-eslint/scope-manager@npm:8.60.0":
|
||||
version: 8.60.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.60.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.60.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.60.1"
|
||||
checksum: 10/7228c110410ff8cfc01e96d8f17c986f8b4dd447fe3a3291baaab8fe946026ccdf0291865f788f18cf538ab49bfc067fe797708b6b8590104a65f7e69f921cc5
|
||||
"@typescript-eslint/types": "npm:8.60.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.60.0"
|
||||
checksum: 10/c08274fdb38be51d2d655ee32bed271cfedf5f5775709da98b3d6cf5f7eb419e98228fb087b48f4a591f4dd71ebcb27a8bd716fa831442c7cad708288625e454
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/tsconfig-utils@npm:8.60.1, @typescript-eslint/tsconfig-utils@npm:^8.60.1":
|
||||
version: 8.60.1
|
||||
resolution: "@typescript-eslint/tsconfig-utils@npm:8.60.1"
|
||||
"@typescript-eslint/tsconfig-utils@npm:8.60.0, @typescript-eslint/tsconfig-utils@npm:^8.60.0":
|
||||
version: 8.60.0
|
||||
resolution: "@typescript-eslint/tsconfig-utils@npm:8.60.0"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/afc78b19b856a71dc4e493f931ae44e1a91dc6441a14cb92e4063db880892f3874768f9d347d4b2f45362f2090e4455407c70f42027d77ddc85d6cba95cdb76c
|
||||
checksum: 10/d82cac7dec0366c0e680d002b4d20bc2564a198a2d9a80099f4fa7ee2b2f394dd2d47df03f1c4b276c4de6c7b8684a50e7bad0ddd5b33907188e90cc203a9593
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/type-utils@npm:8.60.1":
|
||||
version: 8.60.1
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.60.1"
|
||||
"@typescript-eslint/type-utils@npm:8.60.0":
|
||||
version: 8.60.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.60.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.60.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.60.1"
|
||||
"@typescript-eslint/utils": "npm:8.60.1"
|
||||
"@typescript-eslint/types": "npm:8.60.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.60.0"
|
||||
"@typescript-eslint/utils": "npm:8.60.0"
|
||||
debug: "npm:^4.4.3"
|
||||
ts-api-utils: "npm:^2.5.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/6f426263be597063831bf308e52328e8d387af5db955a09cb85fde1c72f5b1b36a365133b9c9a74330e5e948e59bf9a9b82605f4c9c4e3bf9b6cb7f4c37e4b18
|
||||
checksum: 10/4b29dcc1ee7a006b2df8a50700b43701bedd4f8380e94311a8988102d98fdd89244c233a8063a800cbdee86278bdc98874bfa6a8a3c71f1b278be1be6698961b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/types@npm:8.60.1, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.60.1":
|
||||
version: 8.60.1
|
||||
resolution: "@typescript-eslint/types@npm:8.60.1"
|
||||
checksum: 10/c603417e621b5b1263c2f60fad9e202d560fd07fce7f40e9a356c0530e5eaf0ff1a9af865237bf93aa18a5a4e2f034ee0cce0fe6c070f08df33e35a099bdea47
|
||||
"@typescript-eslint/types@npm:8.60.0, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.60.0":
|
||||
version: 8.60.0
|
||||
resolution: "@typescript-eslint/types@npm:8.60.0"
|
||||
checksum: 10/8c6967503b3a370af10fea7bfec9adc7a4152e0e8aaa72ee790f105f08721683f6e8829acf610de82bfcdeb56bdf07f6795ccec394edbdac222fd3a1d76fe9cd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:8.60.1":
|
||||
version: 8.60.1
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.60.1"
|
||||
"@typescript-eslint/typescript-estree@npm:8.60.0":
|
||||
version: 8.60.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.60.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/project-service": "npm:8.60.1"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:8.60.1"
|
||||
"@typescript-eslint/types": "npm:8.60.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.60.1"
|
||||
"@typescript-eslint/project-service": "npm:8.60.0"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:8.60.0"
|
||||
"@typescript-eslint/types": "npm:8.60.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.60.0"
|
||||
debug: "npm:^4.4.3"
|
||||
minimatch: "npm:^10.2.2"
|
||||
semver: "npm:^7.7.3"
|
||||
@@ -4654,32 +4654,32 @@ __metadata:
|
||||
ts-api-utils: "npm:^2.5.0"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/9c3a56266aadf589bc6e770cd04cb3f55b1ee1507dcacda61866408c656ae4462aa7e11baf39eb939bc4d1e3b843cf58e60f3ebdeb3e75f042ff0f6fb39c311b
|
||||
checksum: 10/ad02384fd48152a7d9bb5db1aa5d6cbda1cfa9e549a2d529d801ec1401d1d7d011c5e071f5b4d99c5ed656c95e5e97c46a783b45dcc7c016df7fee37ab5bdc0a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/utils@npm:8.60.1":
|
||||
version: 8.60.1
|
||||
resolution: "@typescript-eslint/utils@npm:8.60.1"
|
||||
"@typescript-eslint/utils@npm:8.60.0":
|
||||
version: 8.60.0
|
||||
resolution: "@typescript-eslint/utils@npm:8.60.0"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": "npm:^4.9.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.60.1"
|
||||
"@typescript-eslint/types": "npm:8.60.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.60.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.60.0"
|
||||
"@typescript-eslint/types": "npm:8.60.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.60.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/a75f8714995b6280b4c15ca957bbc6634862453461111e4a2a07b8bc72b51a504484a9b957fc5b7a646c4bf09f1e414a0c52cd3b6798c42fb8c4de83b1b5a364
|
||||
checksum: 10/9fc8bc7a62deacd3823d957de8e8ca2012ffa90715734cd89d0e3a62c2c9e2775d3ba9da80e419339893a44af8674d690488cb195c981e8de9fd9dfa4948956d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:8.60.1":
|
||||
version: 8.60.1
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.60.1"
|
||||
"@typescript-eslint/visitor-keys@npm:8.60.0":
|
||||
version: 8.60.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.60.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.60.1"
|
||||
"@typescript-eslint/types": "npm:8.60.0"
|
||||
eslint-visitor-keys: "npm:^5.0.0"
|
||||
checksum: 10/6d120b4a790477ae0291e69f6457686c71b929cc40519148f6b6c7fbc09604b15821ae8cf1005aa23acec5105b4016db256a68d68f30eda8d6c24d4fdb0ede86
|
||||
checksum: 10/4854d08416e2c97837cc1ecf8dacb50b3337ebb34bd6d703ad40b6585fdf78243074e56994ddc90650586146cebd6ad7390b6fa3ddda4e3532be4b872dd8f541
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -5641,12 +5641,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"barcode-detector@npm:3.2.0":
|
||||
version: 3.2.0
|
||||
resolution: "barcode-detector@npm:3.2.0"
|
||||
"barcode-detector@npm:3.1.3":
|
||||
version: 3.1.3
|
||||
resolution: "barcode-detector@npm:3.1.3"
|
||||
dependencies:
|
||||
zxing-wasm: "npm:3.1.0"
|
||||
checksum: 10/f52eb18ddae2af3d4c9c76b47e7b639d0834cd32f558901d8a23cc00349047e53a6da5c3958653fa524dcb912ed9178f3e0d37939b9be00f9607772a84d90ccf
|
||||
zxing-wasm: "npm:3.0.3"
|
||||
checksum: 10/3e33b00bdc4b6f6bae67ca2a2fbe7def8861060591bf5a46ece6a8f30eadc9c66ce32776633107bb704ad0910302845a6bd520045e169410ec7ad8d4346633a7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -7957,10 +7957,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fuse.js@npm:7.4.1":
|
||||
version: 7.4.1
|
||||
resolution: "fuse.js@npm:7.4.1"
|
||||
checksum: 10/581941d5015968ee624feb10a56d9b49d5d954672b2c9ec189d4ca513da6f8a3dea2d5f6637386d8298ffc5846f6d83435210d40a47c58e14d11dc5707544c75
|
||||
"fuse.js@npm:7.4.0":
|
||||
version: 7.4.0
|
||||
resolution: "fuse.js@npm:7.4.0"
|
||||
checksum: 10/dba0ef239be1f28ba5daefb3a17371c73291f4d0db3d1733b625848a7311e05aa58a795cd5b2fd9626c09857608a74e8c9620f5d1ce2d3d0b2d40155ae15e21e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -8483,12 +8483,12 @@ __metadata:
|
||||
"@octokit/rest": "npm:22.0.1"
|
||||
"@replit/codemirror-indentation-markers": "npm:6.5.3"
|
||||
"@rsdoctor/rspack-plugin": "npm:1.5.12"
|
||||
"@rspack/core": "npm:2.0.6"
|
||||
"@rspack/core": "npm:2.0.5"
|
||||
"@rspack/dev-server": "npm:2.0.3"
|
||||
"@swc/helpers": "npm:0.5.23"
|
||||
"@thomasloven/round-slider": "npm:0.6.0"
|
||||
"@tsparticles/engine": "npm:4.1.2"
|
||||
"@tsparticles/preset-links": "npm:4.1.2"
|
||||
"@tsparticles/engine": "npm:4.1.1"
|
||||
"@tsparticles/preset-links": "npm:4.1.1"
|
||||
"@types/chromecast-caf-receiver": "npm:6.0.26"
|
||||
"@types/chromecast-caf-sender": "npm:1.0.11"
|
||||
"@types/color-name": "npm:2.0.0"
|
||||
@@ -8509,7 +8509,7 @@ __metadata:
|
||||
"@webcomponents/webcomponentsjs": "npm:2.8.0"
|
||||
babel-loader: "npm:10.1.1"
|
||||
babel-plugin-template-html-minifier: "npm:4.1.0"
|
||||
barcode-detector: "npm:3.2.0"
|
||||
barcode-detector: "npm:3.1.3"
|
||||
browserslist-useragent-regexp: "npm:4.1.4"
|
||||
cally: "npm:0.9.2"
|
||||
color-name: "npm:2.1.0"
|
||||
@@ -8534,7 +8534,7 @@ __metadata:
|
||||
eslint-plugin-wc: "npm:3.1.0"
|
||||
fancy-log: "npm:2.0.0"
|
||||
fs-extra: "npm:11.3.5"
|
||||
fuse.js: "npm:7.4.1"
|
||||
fuse.js: "npm:7.4.0"
|
||||
generate-license-file: "npm:4.2.1"
|
||||
glob: "npm:13.0.6"
|
||||
globals: "npm:17.6.0"
|
||||
@@ -8548,7 +8548,7 @@ __metadata:
|
||||
home-assistant-js-websocket: "npm:9.6.0"
|
||||
html-minifier-terser: "npm:7.2.0"
|
||||
husky: "npm:9.1.7"
|
||||
idb-keyval: "npm:6.2.5"
|
||||
idb-keyval: "npm:6.2.4"
|
||||
intl-messageformat: "npm:11.2.7"
|
||||
js-yaml: "npm:4.2.0"
|
||||
jsdom: "npm:29.1.1"
|
||||
@@ -8582,12 +8582,12 @@ __metadata:
|
||||
sortablejs: "patch:sortablejs@npm%3A1.15.6#~/.yarn/patches/sortablejs-npm-1.15.6-3235a8f83b.patch"
|
||||
stacktrace-js: "npm:2.0.2"
|
||||
superstruct: "npm:2.0.2"
|
||||
tar: "npm:7.5.16"
|
||||
tar: "npm:7.5.15"
|
||||
terser-webpack-plugin: "npm:5.6.1"
|
||||
tinykeys: "patch:tinykeys@npm%3A4.0.0#~/.yarn/patches/tinykeys-npm-4.0.0-a6ca3fd771.patch"
|
||||
tinykeys: "npm:4.0.0"
|
||||
ts-lit-plugin: "npm:2.0.2"
|
||||
typescript: "npm:6.0.3"
|
||||
typescript-eslint: "npm:8.60.1"
|
||||
typescript-eslint: "npm:8.60.0"
|
||||
vite-tsconfig-paths: "npm:6.1.1"
|
||||
vitest: "npm:4.1.8"
|
||||
webpack-stats-plugin: "npm:1.1.3"
|
||||
@@ -8762,10 +8762,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"idb-keyval@npm:6.2.5":
|
||||
version: 6.2.5
|
||||
resolution: "idb-keyval@npm:6.2.5"
|
||||
checksum: 10/ac645882b3258ff07347d085baab91b871bac7be4f46ff8e20a7c036c2df35d3f695a30050009f27237b99045203568f2a842a35295a48f9b815959ee51a347e
|
||||
"idb-keyval@npm:6.2.4":
|
||||
version: 6.2.4
|
||||
resolution: "idb-keyval@npm:6.2.4"
|
||||
checksum: 10/b1bc874eb582c6bed89dd40a07fe5ca593238b37cded9c604e0cb74b396d2b8caa850519af4467e5ca1b4628682a6102150299db69a393702d0a0718945bc5ec
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -12999,16 +12999,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tar@npm:*, tar@npm:7.5.16, tar@npm:^7.4.3, tar@npm:^7.5.4":
|
||||
version: 7.5.16
|
||||
resolution: "tar@npm:7.5.16"
|
||||
"tar@npm:*, tar@npm:7.5.15, tar@npm:^7.4.3, tar@npm:^7.5.4":
|
||||
version: 7.5.15
|
||||
resolution: "tar@npm:7.5.15"
|
||||
dependencies:
|
||||
"@isaacs/fs-minipass": "npm:^4.0.0"
|
||||
chownr: "npm:^3.0.0"
|
||||
minipass: "npm:^7.1.2"
|
||||
minizlib: "npm:^3.1.0"
|
||||
yallist: "npm:^5.0.0"
|
||||
checksum: 10/fafa22efceb9f056bf29ddc47d9bd90bb82fe3ce57b8d1242fc45771251741964cebba69d4e14a24fd1643f3c7f68478e945a19def534703cf370c2d9dca2e09
|
||||
checksum: 10/b4cb6acd822159867f81ebda8d765c6941ec8292f1cf2f870d3713f4933c14bf0ed7bf4a92338143c31e8815ca0a1fdd62aa03ddb48a42ae187f7ef696583ffe
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -13196,13 +13196,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tinykeys@patch:tinykeys@npm%3A4.0.0#~/.yarn/patches/tinykeys-npm-4.0.0-a6ca3fd771.patch":
|
||||
version: 4.0.0
|
||||
resolution: "tinykeys@patch:tinykeys@npm%3A4.0.0#~/.yarn/patches/tinykeys-npm-4.0.0-a6ca3fd771.patch::version=4.0.0&hash=267e16"
|
||||
checksum: 10/f1ca5d1eef7fc628ba55c53da218869db0d6efc6ba28ebbc4974476ff6f2a29ec34cd584008eabc6f03453cfcd4c7fc59b51d8fbca355fd30a8e784eb3c9bc8d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tinyrainbow@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "tinyrainbow@npm:3.1.0"
|
||||
@@ -13399,12 +13392,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"type-fest@npm:^5.7.0":
|
||||
version: 5.7.0
|
||||
resolution: "type-fest@npm:5.7.0"
|
||||
"type-fest@npm:^5.6.0":
|
||||
version: 5.6.0
|
||||
resolution: "type-fest@npm:5.6.0"
|
||||
dependencies:
|
||||
tagged-tag: "npm:^1.0.0"
|
||||
checksum: 10/4867626aa489968df98e09ecdefbc45dfbb191ae5fb8924b3bd45da9cd940879b387086226366dce028570983a3fbe80adc53ad105a169bbbd27621c496bd6f0
|
||||
checksum: 10/2cc7a510f46a538af7a365ed50cee10e51a69bd353ca66c2a432e172b90ff12efff9ba59c7ba493d6361d07fd298c84945b9e4481ab63f85a29860c0b0430233
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -13461,18 +13454,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript-eslint@npm:8.60.1":
|
||||
version: 8.60.1
|
||||
resolution: "typescript-eslint@npm:8.60.1"
|
||||
"typescript-eslint@npm:8.60.0":
|
||||
version: 8.60.0
|
||||
resolution: "typescript-eslint@npm:8.60.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/eslint-plugin": "npm:8.60.1"
|
||||
"@typescript-eslint/parser": "npm:8.60.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.60.1"
|
||||
"@typescript-eslint/utils": "npm:8.60.1"
|
||||
"@typescript-eslint/eslint-plugin": "npm:8.60.0"
|
||||
"@typescript-eslint/parser": "npm:8.60.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.60.0"
|
||||
"@typescript-eslint/utils": "npm:8.60.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/e12091ab2540b817c76b0ec6aad92e341f810310bec2b24bc95780aee106049c05363998f6ea52ed066130c8afc41dca1627f56e4c1df1dd519f4d4ca0ce4910
|
||||
checksum: 10/625e49e6d06e32adcfe903087d1fb2adc3be925adafe1f4e57f690bb196b35e2aac01760a3d5e17a53ea2feb6fef3a13da4b8faa214f628ec56e64f99f20e4ad
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -14867,14 +14860,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zxing-wasm@npm:3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "zxing-wasm@npm:3.1.0"
|
||||
"zxing-wasm@npm:3.0.3":
|
||||
version: 3.0.3
|
||||
resolution: "zxing-wasm@npm:3.0.3"
|
||||
dependencies:
|
||||
"@types/emscripten": "npm:^1.41.5"
|
||||
type-fest: "npm:^5.7.0"
|
||||
type-fest: "npm:^5.6.0"
|
||||
peerDependencies:
|
||||
"@types/emscripten": ">=1.39.6"
|
||||
checksum: 10/ea68d0cfbe31d8dabcd9b942dcfdb703866c1f76ee0d804fb75f1e49f092a26771575705dd48d69927bc6525f146fd9e35030a5a72341222716d7f62e5f6c788
|
||||
checksum: 10/0c0636fab96a67d7f8540dede8d98b5cbb53c95313cfbe21114e2fd15262d6b860c84467d627d6e768bcdc684d75c7b5b8ef6b3d53d8d14004436e809a1d4b32
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
Reference in New Issue
Block a user