mirror of
				https://github.com/home-assistant/frontend.git
				synced 2025-11-04 00:19:47 +00:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			20240809.0
			...
			add-no-dev
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					e3be190b36 | 
@@ -1,25 +1,28 @@
 | 
			
		||||
[modern]
 | 
			
		||||
# Modern builds target recent browsers supporting the latest features to minimize transpilation, polyfills, etc.
 | 
			
		||||
# It is served to browsers meeting the following requirements:
 | 
			
		||||
# - released in the last year + current alpha/beta versions
 | 
			
		||||
# - Firefox extended support release (ESR)
 | 
			
		||||
# - with global utilization at or above 0.5%
 | 
			
		||||
# - must support dynamic import of ES modules
 | 
			
		||||
# - exclude browsers no longer being maintained
 | 
			
		||||
# - exclude KaiOS, QQ, and UC browsers due to lack of sufficient feature support data
 | 
			
		||||
unreleased versions
 | 
			
		||||
last 1 year
 | 
			
		||||
Firefox ESR
 | 
			
		||||
>= 0.5% and supports es6-module-dynamic-import
 | 
			
		||||
not dead
 | 
			
		||||
# Support for dynamic import is the main litmus test for serving modern builds.
 | 
			
		||||
# Although officially a ES2020 feature, browsers implemented it early, so this
 | 
			
		||||
# enables all of ES2017 and some features in ES2018.
 | 
			
		||||
supports es6-module-dynamic-import
 | 
			
		||||
 | 
			
		||||
# Exclude Safari 11-12 because of a bug in tagged template literals
 | 
			
		||||
# https://bugs.webkit.org/show_bug.cgi?id=190756
 | 
			
		||||
# Note: Dropping version 11 also enables several more ES2018 features
 | 
			
		||||
not Safari < 13
 | 
			
		||||
not iOS < 13
 | 
			
		||||
 | 
			
		||||
# Exclude KaiOS, QQ, and UC browsers due to lack of sufficient feature support data
 | 
			
		||||
# Babel ignores these automatically, but we need here for Webpack to output ESM with dynamic imports
 | 
			
		||||
not KaiOS > 0
 | 
			
		||||
not QQAndroid > 0
 | 
			
		||||
not UCAndroid > 0
 | 
			
		||||
 | 
			
		||||
# Exclude unsupported browsers
 | 
			
		||||
not dead
 | 
			
		||||
 | 
			
		||||
[legacy]
 | 
			
		||||
# Legacy builds are served when modern requirements are not met and support browsers:
 | 
			
		||||
# - released in the last 7 years + current alpha/beta versionss
 | 
			
		||||
# - with global utilization at or above 0.05%
 | 
			
		||||
# - with global utilization above 0.05%
 | 
			
		||||
# The lattermost query ensures that support for popular old browsers is not dropped too early
 | 
			
		||||
# (e.g. IE 11, Android 4.4, or Samsung 4).
 | 
			
		||||
#
 | 
			
		||||
@@ -33,10 +36,4 @@ not UCAndroid > 0
 | 
			
		||||
# As of May 2023, only web sockets must be added to the query.
 | 
			
		||||
unreleased versions
 | 
			
		||||
last 7 years
 | 
			
		||||
>= 0.05% and supports websockets
 | 
			
		||||
 | 
			
		||||
[legacy-sw]
 | 
			
		||||
# Same as legacy plus supports service workers
 | 
			
		||||
unreleased versions
 | 
			
		||||
last 7 years
 | 
			
		||||
>= 0.05% and supports websockets and supports serviceworkers
 | 
			
		||||
> 0.05% and supports websockets
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@
 | 
			
		||||
  "postCreateCommand": "sudo apt update && sudo apt upgrade -y && sudo apt install -y libpcap-dev",
 | 
			
		||||
  "postStartCommand": "script/bootstrap",
 | 
			
		||||
  "containerEnv": {
 | 
			
		||||
    "DEV_CONTAINER": "1",
 | 
			
		||||
    "WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}"
 | 
			
		||||
  },
 | 
			
		||||
  "customizations": {
 | 
			
		||||
 
 | 
			
		||||
@@ -115,7 +115,6 @@
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "unused-imports/no-unused-imports": "error",
 | 
			
		||||
    "lit/attribute-names": "warn",
 | 
			
		||||
    "lit/attribute-value-entities": "off",
 | 
			
		||||
    "lit/no-template-map": "off",
 | 
			
		||||
    "lit/no-native-attributes": "warn",
 | 
			
		||||
@@ -126,5 +125,6 @@
 | 
			
		||||
    "lit-a11y/anchor-is-valid": "warn",
 | 
			
		||||
    "lit-a11y/role-has-required-aria-attrs": "warn"
 | 
			
		||||
  },
 | 
			
		||||
  "plugins": ["unused-imports"]
 | 
			
		||||
  "plugins": ["disable", "unused-imports"],
 | 
			
		||||
  "processor": "disable/disable"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								.github/workflows/cast_deployment.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/cast_deployment.yaml
									
									
									
									
										vendored
									
									
								
							@@ -21,12 +21,12 @@ jobs:
 | 
			
		||||
      url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out files from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.1.3
 | 
			
		||||
        with:
 | 
			
		||||
          ref: dev
 | 
			
		||||
 | 
			
		||||
      - name: Setup Node
 | 
			
		||||
        uses: actions/setup-node@v4.0.3
 | 
			
		||||
        uses: actions/setup-node@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          node-version-file: ".nvmrc"
 | 
			
		||||
          cache: yarn
 | 
			
		||||
@@ -57,12 +57,12 @@ jobs:
 | 
			
		||||
      url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out files from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.1.3
 | 
			
		||||
        with:
 | 
			
		||||
          ref: master
 | 
			
		||||
 | 
			
		||||
      - name: Setup Node
 | 
			
		||||
        uses: actions/setup-node@v4.0.3
 | 
			
		||||
        uses: actions/setup-node@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          node-version-file: ".nvmrc"
 | 
			
		||||
          cache: yarn
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							@@ -24,9 +24,9 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out files from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.1.3
 | 
			
		||||
      - name: Setup Node
 | 
			
		||||
        uses: actions/setup-node@v4.0.3
 | 
			
		||||
        uses: actions/setup-node@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          node-version-file: ".nvmrc"
 | 
			
		||||
          cache: yarn
 | 
			
		||||
@@ -58,9 +58,9 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out files from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.1.3
 | 
			
		||||
      - name: Setup Node
 | 
			
		||||
        uses: actions/setup-node@v4.0.3
 | 
			
		||||
        uses: actions/setup-node@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          node-version-file: ".nvmrc"
 | 
			
		||||
          cache: yarn
 | 
			
		||||
@@ -76,9 +76,9 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out files from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.1.3
 | 
			
		||||
      - name: Setup Node
 | 
			
		||||
        uses: actions/setup-node@v4.0.3
 | 
			
		||||
        uses: actions/setup-node@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          node-version-file: ".nvmrc"
 | 
			
		||||
          cache: yarn
 | 
			
		||||
@@ -89,7 +89,7 @@ jobs:
 | 
			
		||||
        env:
 | 
			
		||||
          IS_TEST: "true"
 | 
			
		||||
      - name: Upload bundle stats
 | 
			
		||||
        uses: actions/upload-artifact@v4.3.5
 | 
			
		||||
        uses: actions/upload-artifact@v4.3.2
 | 
			
		||||
        with:
 | 
			
		||||
          name: frontend-bundle-stats
 | 
			
		||||
          path: build/stats/*.json
 | 
			
		||||
@@ -100,9 +100,9 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out files from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.1.3
 | 
			
		||||
      - name: Setup Node
 | 
			
		||||
        uses: actions/setup-node@v4.0.3
 | 
			
		||||
        uses: actions/setup-node@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          node-version-file: ".nvmrc"
 | 
			
		||||
          cache: yarn
 | 
			
		||||
@@ -113,7 +113,7 @@ jobs:
 | 
			
		||||
        env:
 | 
			
		||||
          IS_TEST: "true"
 | 
			
		||||
      - name: Upload bundle stats
 | 
			
		||||
        uses: actions/upload-artifact@v4.3.5
 | 
			
		||||
        uses: actions/upload-artifact@v4.3.2
 | 
			
		||||
        with:
 | 
			
		||||
          name: supervisor-bundle-stats
 | 
			
		||||
          path: build/stats/*.json
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							@@ -23,7 +23,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout repository
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.1.3
 | 
			
		||||
        with:
 | 
			
		||||
          # We must fetch at least the immediate parents so that if this is
 | 
			
		||||
          # a pull request then we can checkout the head.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								.github/workflows/demo_deployment.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/demo_deployment.yaml
									
									
									
									
										vendored
									
									
								
							@@ -22,12 +22,12 @@ jobs:
 | 
			
		||||
      url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out files from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.1.3
 | 
			
		||||
        with:
 | 
			
		||||
          ref: dev
 | 
			
		||||
 | 
			
		||||
      - name: Setup Node
 | 
			
		||||
        uses: actions/setup-node@v4.0.3
 | 
			
		||||
        uses: actions/setup-node@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          node-version-file: ".nvmrc"
 | 
			
		||||
          cache: yarn
 | 
			
		||||
@@ -58,12 +58,12 @@ jobs:
 | 
			
		||||
      url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out files from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.1.3
 | 
			
		||||
        with:
 | 
			
		||||
          ref: master
 | 
			
		||||
 | 
			
		||||
      - name: Setup Node
 | 
			
		||||
        uses: actions/setup-node@v4.0.3
 | 
			
		||||
        uses: actions/setup-node@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          node-version-file: ".nvmrc"
 | 
			
		||||
          cache: yarn
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/design_deployment.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/design_deployment.yaml
									
									
									
									
										vendored
									
									
								
							@@ -16,10 +16,10 @@ jobs:
 | 
			
		||||
      url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out files from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.1.3
 | 
			
		||||
 | 
			
		||||
      - name: Setup Node
 | 
			
		||||
        uses: actions/setup-node@v4.0.3
 | 
			
		||||
        uses: actions/setup-node@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          node-version-file: ".nvmrc"
 | 
			
		||||
          cache: yarn
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/design_preview.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/design_preview.yaml
									
									
									
									
										vendored
									
									
								
							@@ -21,10 +21,10 @@ jobs:
 | 
			
		||||
    if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out files from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.1.3
 | 
			
		||||
 | 
			
		||||
      - name: Setup Node
 | 
			
		||||
        uses: actions/setup-node@v4.0.3
 | 
			
		||||
        uses: actions/setup-node@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          node-version-file: ".nvmrc"
 | 
			
		||||
          cache: yarn
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								.github/workflows/nightly.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/nightly.yaml
									
									
									
									
										vendored
									
									
								
							@@ -20,7 +20,7 @@ jobs:
 | 
			
		||||
      contents: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.1.3
 | 
			
		||||
 | 
			
		||||
      - name: Set up Python ${{ env.PYTHON_VERSION }}
 | 
			
		||||
        uses: actions/setup-python@v5
 | 
			
		||||
@@ -28,7 +28,7 @@ jobs:
 | 
			
		||||
          python-version: ${{ env.PYTHON_VERSION }}
 | 
			
		||||
 | 
			
		||||
      - name: Setup Node
 | 
			
		||||
        uses: actions/setup-node@v4.0.3
 | 
			
		||||
        uses: actions/setup-node@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          node-version-file: ".nvmrc"
 | 
			
		||||
          cache: yarn
 | 
			
		||||
@@ -57,14 +57,14 @@ jobs:
 | 
			
		||||
        run: tar -czvf translations.tar.gz translations
 | 
			
		||||
 | 
			
		||||
      - name: Upload build artifacts
 | 
			
		||||
        uses: actions/upload-artifact@v4.3.5
 | 
			
		||||
        uses: actions/upload-artifact@v4.3.2
 | 
			
		||||
        with:
 | 
			
		||||
          name: wheels
 | 
			
		||||
          path: dist/home_assistant_frontend*.whl
 | 
			
		||||
          if-no-files-found: error
 | 
			
		||||
 | 
			
		||||
      - name: Upload translations
 | 
			
		||||
        uses: actions/upload-artifact@v4.3.5
 | 
			
		||||
        uses: actions/upload-artifact@v4.3.2
 | 
			
		||||
        with:
 | 
			
		||||
          name: translations
 | 
			
		||||
          path: translations.tar.gz
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/relative-ci.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/relative-ci.yaml
									
									
									
									
										vendored
									
									
								
							@@ -17,7 +17,7 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Send bundle stats and build information to RelativeCI
 | 
			
		||||
        uses: relative-ci/agent-action@v2.1.11
 | 
			
		||||
        uses: relative-ci/agent-action@v2.1.10
 | 
			
		||||
        with:
 | 
			
		||||
          key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }}
 | 
			
		||||
          token: ${{ github.token }}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
								
							@@ -23,7 +23,7 @@ jobs:
 | 
			
		||||
      contents: write # Required to upload release assets
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.1.3
 | 
			
		||||
 | 
			
		||||
      - name: Verify version
 | 
			
		||||
        uses: home-assistant/actions/helpers/verify-version@master
 | 
			
		||||
@@ -34,7 +34,7 @@ jobs:
 | 
			
		||||
          python-version: ${{ env.PYTHON_VERSION }}
 | 
			
		||||
 | 
			
		||||
      - name: Setup Node
 | 
			
		||||
        uses: actions/setup-node@v4.0.3
 | 
			
		||||
        uses: actions/setup-node@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          node-version-file: ".nvmrc"
 | 
			
		||||
          cache: yarn
 | 
			
		||||
@@ -55,7 +55,7 @@ jobs:
 | 
			
		||||
          script/release
 | 
			
		||||
 | 
			
		||||
      - name: Upload release assets
 | 
			
		||||
        uses: softprops/action-gh-release@v2.0.8
 | 
			
		||||
        uses: softprops/action-gh-release@v2.0.4
 | 
			
		||||
        with:
 | 
			
		||||
          files: |
 | 
			
		||||
            dist/*.whl
 | 
			
		||||
@@ -74,9 +74,9 @@ jobs:
 | 
			
		||||
          echo "home-assistant-frontend==$version" > ./requirements.txt
 | 
			
		||||
 | 
			
		||||
      - name: Build wheels
 | 
			
		||||
        uses: home-assistant/wheels@2024.07.1
 | 
			
		||||
        uses: home-assistant/wheels@2024.01.0
 | 
			
		||||
        with:
 | 
			
		||||
          abi: cp312
 | 
			
		||||
          abi: cp311
 | 
			
		||||
          tag: musllinux_1_2
 | 
			
		||||
          arch: amd64
 | 
			
		||||
          wheels-key: ${{ secrets.WHEELS_KEY }}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/translations.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/translations.yaml
									
									
									
									
										vendored
									
									
								
							@@ -13,7 +13,7 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.1.3
 | 
			
		||||
 | 
			
		||||
      - name: Upload Translations
 | 
			
		||||
        run: |
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1,4 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
. "$(dirname -- "$0")/_/husky.sh"
 | 
			
		||||
 | 
			
		||||
yarn run lint-staged --relative --shell "/bin/bash"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,55 +0,0 @@
 | 
			
		||||
diff --git a/build/inject-manifest.js b/build/inject-manifest.js
 | 
			
		||||
index 60e3d2bb51c11a19fbbedbad65e101082ec41c36..fed6026630f43f86e25446383982cf6fb694313b 100644
 | 
			
		||||
--- a/build/inject-manifest.js
 | 
			
		||||
+++ b/build/inject-manifest.js
 | 
			
		||||
@@ -104,7 +104,7 @@ async function injectManifest(config) {
 | 
			
		||||
             replaceString: manifestString,
 | 
			
		||||
             searchString: options.injectionPoint,
 | 
			
		||||
         });
 | 
			
		||||
-        filesToWrite[options.swDest] = source;
 | 
			
		||||
+        filesToWrite[options.swDest] = source.replace(url, encodeURI(upath_1.default.basename(destPath)));
 | 
			
		||||
         filesToWrite[destPath] = map;
 | 
			
		||||
     }
 | 
			
		||||
     else {
 | 
			
		||||
diff --git a/build/lib/translate-url-to-sourcemap-paths.js b/build/lib/translate-url-to-sourcemap-paths.js
 | 
			
		||||
index 3220c5474eeac6e8a56ca9b2ac2bd9be48529e43..5f003879a904d4840529a42dd056d288fd213771 100644
 | 
			
		||||
--- a/build/lib/translate-url-to-sourcemap-paths.js
 | 
			
		||||
+++ b/build/lib/translate-url-to-sourcemap-paths.js
 | 
			
		||||
@@ -22,7 +22,7 @@ function translateURLToSourcemapPaths(url, swSrc, swDest) {
 | 
			
		||||
         const possibleSrcPath = upath_1.default.resolve(upath_1.default.dirname(swSrc), url);
 | 
			
		||||
         if (fs_extra_1.default.existsSync(possibleSrcPath)) {
 | 
			
		||||
             srcPath = possibleSrcPath;
 | 
			
		||||
-            destPath = upath_1.default.resolve(upath_1.default.dirname(swDest), url);
 | 
			
		||||
+            destPath = `${swDest}.map`;
 | 
			
		||||
         }
 | 
			
		||||
         else {
 | 
			
		||||
             warning = `${errors_1.errors['cant-find-sourcemap']} ${possibleSrcPath}`;
 | 
			
		||||
diff --git a/src/inject-manifest.ts b/src/inject-manifest.ts
 | 
			
		||||
index 8795ddcaa77aea7b0356417e4bc4b19e2b3f860c..fcdc68342d9ac53936c9ed40a9ccfc2f5070cad3 100644
 | 
			
		||||
--- a/src/inject-manifest.ts
 | 
			
		||||
+++ b/src/inject-manifest.ts
 | 
			
		||||
@@ -129,7 +129,10 @@ export async function injectManifest(
 | 
			
		||||
       searchString: options.injectionPoint!,
 | 
			
		||||
     });
 | 
			
		||||
 
 | 
			
		||||
-    filesToWrite[options.swDest] = source;
 | 
			
		||||
+    filesToWrite[options.swDest] = source.replace(
 | 
			
		||||
+      url!,
 | 
			
		||||
+      encodeURI(upath.basename(destPath)),
 | 
			
		||||
+    );
 | 
			
		||||
     filesToWrite[destPath] = map;
 | 
			
		||||
   } else {
 | 
			
		||||
     // If there's no sourcemap associated with swSrc, a simple string
 | 
			
		||||
diff --git a/src/lib/translate-url-to-sourcemap-paths.ts b/src/lib/translate-url-to-sourcemap-paths.ts
 | 
			
		||||
index 072eac40d4ef5d095a01cb7f7e392a9e034853bd..f0bbe69e88ef3a415de18a7e9cb264daea273d71 100644
 | 
			
		||||
--- a/src/lib/translate-url-to-sourcemap-paths.ts
 | 
			
		||||
+++ b/src/lib/translate-url-to-sourcemap-paths.ts
 | 
			
		||||
@@ -28,7 +28,7 @@ export function translateURLToSourcemapPaths(
 | 
			
		||||
     const possibleSrcPath = upath.resolve(upath.dirname(swSrc), url);
 | 
			
		||||
     if (fse.existsSync(possibleSrcPath)) {
 | 
			
		||||
       srcPath = possibleSrcPath;
 | 
			
		||||
-      destPath = upath.resolve(upath.dirname(swDest), url);
 | 
			
		||||
+      destPath = `${swDest}.map`;
 | 
			
		||||
     } else {
 | 
			
		||||
       warning = `${errors['cant-find-sourcemap']} ${possibleSrcPath}`;
 | 
			
		||||
     }
 | 
			
		||||
							
								
								
									
										893
									
								
								.yarn/releases/yarn-4.1.1.cjs
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										893
									
								
								.yarn/releases/yarn-4.1.1.cjs
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										925
									
								
								.yarn/releases/yarn-4.4.0.cjs
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										925
									
								
								.yarn/releases/yarn-4.4.0.cjs
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -6,4 +6,4 @@ enableGlobalCache: false
 | 
			
		||||
 | 
			
		||||
nodeLinker: node-modules
 | 
			
		||||
 | 
			
		||||
yarnPath: .yarn/releases/yarn-4.4.0.cjs
 | 
			
		||||
yarnPath: .yarn/releases/yarn-4.1.1.cjs
 | 
			
		||||
 
 | 
			
		||||
@@ -1,56 +1,7 @@
 | 
			
		||||
import defineProvider from "@babel/helper-define-polyfill-provider";
 | 
			
		||||
import { join } from "node:path";
 | 
			
		||||
import paths from "../paths.cjs";
 | 
			
		||||
 | 
			
		||||
const POLYFILL_DIR = join(paths.polymer_dir, "src/resources/polyfills");
 | 
			
		||||
 | 
			
		||||
// List of polyfill keys with supported browser targets for the functionality
 | 
			
		||||
const PolyfillSupport = {
 | 
			
		||||
  // Note states and shadowRoot properties should be supported.
 | 
			
		||||
  "element-internals": {
 | 
			
		||||
    android: 90,
 | 
			
		||||
    chrome: 90,
 | 
			
		||||
    edge: 90,
 | 
			
		||||
    firefox: 126,
 | 
			
		||||
    ios: 17.4,
 | 
			
		||||
    opera: 76,
 | 
			
		||||
    opera_mobile: 64,
 | 
			
		||||
    safari: 17.4,
 | 
			
		||||
    samsung: 15.0,
 | 
			
		||||
  },
 | 
			
		||||
  "element-append": {
 | 
			
		||||
    android: 54,
 | 
			
		||||
    chrome: 54,
 | 
			
		||||
    edge: 17,
 | 
			
		||||
    firefox: 49,
 | 
			
		||||
    ios: 10.0,
 | 
			
		||||
    opera: 41,
 | 
			
		||||
    opera_mobile: 41,
 | 
			
		||||
    safari: 10.0,
 | 
			
		||||
    samsung: 6.0,
 | 
			
		||||
  },
 | 
			
		||||
  "element-getattributenames": {
 | 
			
		||||
    android: 61,
 | 
			
		||||
    chrome: 61,
 | 
			
		||||
    edge: 18,
 | 
			
		||||
    firefox: 45,
 | 
			
		||||
    ios: 10.3,
 | 
			
		||||
    opera: 48,
 | 
			
		||||
    opera_mobile: 45,
 | 
			
		||||
    safari: 10.1,
 | 
			
		||||
    samsung: 8.0,
 | 
			
		||||
  },
 | 
			
		||||
  "element-toggleattribute": {
 | 
			
		||||
    android: 69,
 | 
			
		||||
    chrome: 69,
 | 
			
		||||
    edge: 18,
 | 
			
		||||
    firefox: 63,
 | 
			
		||||
    ios: 12.0,
 | 
			
		||||
    opera: 56,
 | 
			
		||||
    opera_mobile: 48,
 | 
			
		||||
    safari: 12.0,
 | 
			
		||||
    samsung: 10.0,
 | 
			
		||||
  },
 | 
			
		||||
  fetch: {
 | 
			
		||||
    android: 42,
 | 
			
		||||
    chrome: 42,
 | 
			
		||||
@@ -62,31 +13,6 @@ const PolyfillSupport = {
 | 
			
		||||
    safari: 10.1,
 | 
			
		||||
    samsung: 4.0,
 | 
			
		||||
  },
 | 
			
		||||
  "intl-getcanonicallocales": {
 | 
			
		||||
    android: 54,
 | 
			
		||||
    chrome: 54,
 | 
			
		||||
    edge: 16,
 | 
			
		||||
    firefox: 48,
 | 
			
		||||
    ios: 10.3,
 | 
			
		||||
    opera: 41,
 | 
			
		||||
    opera_mobile: 41,
 | 
			
		||||
    safari: 10.1,
 | 
			
		||||
    samsung: 6.0,
 | 
			
		||||
  },
 | 
			
		||||
  "intl-locale": {
 | 
			
		||||
    android: 74,
 | 
			
		||||
    chrome: 74,
 | 
			
		||||
    edge: 79,
 | 
			
		||||
    firefox: 75,
 | 
			
		||||
    ios: 14.0,
 | 
			
		||||
    opera: 62,
 | 
			
		||||
    opera_mobile: 53,
 | 
			
		||||
    safari: 14.0,
 | 
			
		||||
    samsung: 11.0,
 | 
			
		||||
  },
 | 
			
		||||
  "intl-other": {
 | 
			
		||||
    // Not specified (i.e. always try polyfill) since compatibility depends on supported locales
 | 
			
		||||
  },
 | 
			
		||||
  proxy: {
 | 
			
		||||
    android: 49,
 | 
			
		||||
    chrome: 49,
 | 
			
		||||
@@ -98,67 +24,17 @@ const PolyfillSupport = {
 | 
			
		||||
    safari: 10.0,
 | 
			
		||||
    samsung: 5.0,
 | 
			
		||||
  },
 | 
			
		||||
  "resize-observer": {
 | 
			
		||||
    android: 64,
 | 
			
		||||
    chrome: 64,
 | 
			
		||||
    edge: 79,
 | 
			
		||||
    firefox: 69,
 | 
			
		||||
    ios: 13.4,
 | 
			
		||||
    opera: 51,
 | 
			
		||||
    opera_mobile: 47,
 | 
			
		||||
    safari: 13.1,
 | 
			
		||||
    samsung: 9.0,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Map of global variables and/or instance and static properties to the
 | 
			
		||||
// corresponding polyfill key and actual module to import
 | 
			
		||||
const polyfillMap = {
 | 
			
		||||
  global: {
 | 
			
		||||
    fetch: { key: "fetch", module: "unfetch/polyfill" },
 | 
			
		||||
    Proxy: { key: "proxy", module: "proxy-polyfill" },
 | 
			
		||||
    ResizeObserver: {
 | 
			
		||||
      key: "resize-observer",
 | 
			
		||||
      module: join(POLYFILL_DIR, "resize-observer.ts"),
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  instance: {
 | 
			
		||||
    attachInternals: {
 | 
			
		||||
      key: "element-internals",
 | 
			
		||||
      module: "element-internals-polyfill",
 | 
			
		||||
    },
 | 
			
		||||
    ...Object.fromEntries(
 | 
			
		||||
      ["append", "getAttributeNames", "toggleAttribute"].map((prop) => {
 | 
			
		||||
        const key = `element-${prop.toLowerCase()}`;
 | 
			
		||||
        return [prop, { key, module: join(POLYFILL_DIR, `${key}.ts`) }];
 | 
			
		||||
      })
 | 
			
		||||
    ),
 | 
			
		||||
  },
 | 
			
		||||
  static: {
 | 
			
		||||
    Intl: {
 | 
			
		||||
      getCanonicalLocales: {
 | 
			
		||||
        key: "intl-getcanonicallocales",
 | 
			
		||||
        module: join(POLYFILL_DIR, "intl-polyfill.ts"),
 | 
			
		||||
      },
 | 
			
		||||
      Locale: {
 | 
			
		||||
        key: "intl-locale",
 | 
			
		||||
        module: join(POLYFILL_DIR, "intl-polyfill.ts"),
 | 
			
		||||
      },
 | 
			
		||||
      ...Object.fromEntries(
 | 
			
		||||
        [
 | 
			
		||||
          "DateTimeFormat",
 | 
			
		||||
          "DisplayNames",
 | 
			
		||||
          "ListFormat",
 | 
			
		||||
          "NumberFormat",
 | 
			
		||||
          "PluralRules",
 | 
			
		||||
          "RelativeTimeFormat",
 | 
			
		||||
        ].map((obj) => [
 | 
			
		||||
          obj,
 | 
			
		||||
          { key: "intl-other", module: join(POLYFILL_DIR, "intl-polyfill.ts") },
 | 
			
		||||
        ])
 | 
			
		||||
      ),
 | 
			
		||||
    },
 | 
			
		||||
    fetch: { key: "fetch", module: "unfetch/polyfill" },
 | 
			
		||||
  },
 | 
			
		||||
  instance: {},
 | 
			
		||||
  static: {},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Create plugin using the same factory as for CoreJS
 | 
			
		||||
@@ -166,16 +42,14 @@ export default defineProvider(
 | 
			
		||||
  ({ createMetaResolver, debug, shouldInjectPolyfill }) => {
 | 
			
		||||
    const resolvePolyfill = createMetaResolver(polyfillMap);
 | 
			
		||||
    return {
 | 
			
		||||
      name: "custom-polyfill",
 | 
			
		||||
      name: "HA Custom",
 | 
			
		||||
      polyfills: PolyfillSupport,
 | 
			
		||||
      usageGlobal(meta, utils) {
 | 
			
		||||
        const polyfill = resolvePolyfill(meta);
 | 
			
		||||
        if (polyfill && shouldInjectPolyfill(polyfill.desc.key)) {
 | 
			
		||||
          debug(polyfill.desc.key);
 | 
			
		||||
          utils.injectGlobalImport(polyfill.desc.module);
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,6 @@ const env = require("./env.cjs");
 | 
			
		||||
const paths = require("./paths.cjs");
 | 
			
		||||
const { dependencies } = require("../package.json");
 | 
			
		||||
 | 
			
		||||
const BABEL_PLUGINS = path.join(__dirname, "babel-plugins");
 | 
			
		||||
 | 
			
		||||
// GitHub base URL to use for production source maps
 | 
			
		||||
// Nightly builds use the commit SHA, otherwise assumes there is a tag that matches the version
 | 
			
		||||
module.exports.sourceMapURL = () => {
 | 
			
		||||
@@ -47,7 +45,7 @@ module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
 | 
			
		||||
 | 
			
		||||
module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
 | 
			
		||||
  __DEV__: !isProdBuild,
 | 
			
		||||
  __BUILD__: JSON.stringify(latestBuild ? "modern" : "legacy"),
 | 
			
		||||
  __BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
 | 
			
		||||
  __VERSION__: JSON.stringify(env.version()),
 | 
			
		||||
  __DEMO__: false,
 | 
			
		||||
  __SUPERVISOR__: false,
 | 
			
		||||
@@ -79,12 +77,7 @@ module.exports.terserOptions = ({ latestBuild, isTestBuild }) => ({
 | 
			
		||||
  sourceMap: !isTestBuild,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
module.exports.babelOptions = ({
 | 
			
		||||
  latestBuild,
 | 
			
		||||
  isProdBuild,
 | 
			
		||||
  isTestBuild,
 | 
			
		||||
  sw,
 | 
			
		||||
}) => ({
 | 
			
		||||
module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
 | 
			
		||||
  babelrc: false,
 | 
			
		||||
  compact: false,
 | 
			
		||||
  assumptions: {
 | 
			
		||||
@@ -92,13 +85,13 @@ module.exports.babelOptions = ({
 | 
			
		||||
    setPublicClassFields: true,
 | 
			
		||||
    setSpreadProperties: true,
 | 
			
		||||
  },
 | 
			
		||||
  browserslistEnv: latestBuild ? "modern" : `legacy${sw ? "-sw" : ""}`,
 | 
			
		||||
  browserslistEnv: latestBuild ? "modern" : "legacy",
 | 
			
		||||
  presets: [
 | 
			
		||||
    [
 | 
			
		||||
      "@babel/preset-env",
 | 
			
		||||
      {
 | 
			
		||||
        useBuiltIns: "usage",
 | 
			
		||||
        corejs: dependencies["core-js"],
 | 
			
		||||
        useBuiltIns: latestBuild ? false : "usage",
 | 
			
		||||
        corejs: latestBuild ? false : dependencies["core-js"],
 | 
			
		||||
        bugfixes: true,
 | 
			
		||||
        shippedProposals: true,
 | 
			
		||||
      },
 | 
			
		||||
@@ -107,12 +100,22 @@ module.exports.babelOptions = ({
 | 
			
		||||
  ],
 | 
			
		||||
  plugins: [
 | 
			
		||||
    [
 | 
			
		||||
      path.join(BABEL_PLUGINS, "inline-constants-plugin.cjs"),
 | 
			
		||||
      path.resolve(
 | 
			
		||||
        paths.polymer_dir,
 | 
			
		||||
        "build-scripts/babel-plugins/inline-constants-plugin.cjs"
 | 
			
		||||
      ),
 | 
			
		||||
      {
 | 
			
		||||
        modules: ["@mdi/js"],
 | 
			
		||||
        ignoreModuleNotFound: true,
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
      path.resolve(
 | 
			
		||||
        paths.polymer_dir,
 | 
			
		||||
        "build-scripts/babel-plugins/custom-polyfill-plugin.js"
 | 
			
		||||
      ),
 | 
			
		||||
      { method: "usage-global" },
 | 
			
		||||
    ],
 | 
			
		||||
    // Minify template literals for production
 | 
			
		||||
    isProdBuild && [
 | 
			
		||||
      "template-html-minifier",
 | 
			
		||||
@@ -140,14 +143,8 @@ module.exports.babelOptions = ({
 | 
			
		||||
      "@babel/plugin-transform-runtime",
 | 
			
		||||
      { version: dependencies["@babel/runtime"] },
 | 
			
		||||
    ],
 | 
			
		||||
    // Transpile decorators (still in TC39 process)
 | 
			
		||||
    // Modern browsers support class fields and private methods, but transform is required with the older decorator version dictated by Lit
 | 
			
		||||
    [
 | 
			
		||||
      "@babel/plugin-proposal-decorators",
 | 
			
		||||
      { version: "2018-09", decoratorsBeforeExport: true },
 | 
			
		||||
    ],
 | 
			
		||||
    "@babel/plugin-transform-class-properties",
 | 
			
		||||
    "@babel/plugin-transform-private-methods",
 | 
			
		||||
    // Support  some proposals still in TC39 process
 | 
			
		||||
    ["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],
 | 
			
		||||
  ].filter(Boolean),
 | 
			
		||||
  exclude: [
 | 
			
		||||
    // \\ for Windows, / for Mac OS and Linux
 | 
			
		||||
@@ -156,27 +153,6 @@ module.exports.babelOptions = ({
 | 
			
		||||
  ],
 | 
			
		||||
  sourceMaps: !isTestBuild,
 | 
			
		||||
  overrides: [
 | 
			
		||||
    {
 | 
			
		||||
      // Add plugin to inject various polyfills, excluding the polyfills
 | 
			
		||||
      // themselves to prevent self-injection.
 | 
			
		||||
      plugins: [
 | 
			
		||||
        [
 | 
			
		||||
          path.join(BABEL_PLUGINS, "custom-polyfill-plugin.js"),
 | 
			
		||||
          { method: "usage-global" },
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      exclude: [
 | 
			
		||||
        path.join(paths.polymer_dir, "src/resources/polyfills"),
 | 
			
		||||
        ...[
 | 
			
		||||
          "@formatjs/(?:ecma402-abstract|intl-\\w+)",
 | 
			
		||||
          "@lit-labs/virtualizer/polyfills",
 | 
			
		||||
          "@webcomponents/scoped-custom-element-registry",
 | 
			
		||||
          "element-internals-polyfill",
 | 
			
		||||
          "proxy-polyfill",
 | 
			
		||||
          "unfetch",
 | 
			
		||||
        ].map((p) => new RegExp(`/node_modules/${p}/`)),
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      // Use unambiguous for dependencies so that require() is correctly injected into CommonJS files
 | 
			
		||||
      // Exclusions are needed in some cases where ES modules have no static imports or exports, such as polyfills
 | 
			
		||||
@@ -226,13 +202,7 @@ module.exports.config = {
 | 
			
		||||
    return {
 | 
			
		||||
      name: "frontend" + nameSuffix(latestBuild),
 | 
			
		||||
      entry: {
 | 
			
		||||
        "service-worker":
 | 
			
		||||
          !env.useRollup() && !latestBuild
 | 
			
		||||
            ? {
 | 
			
		||||
                import: "./src/entrypoints/service-worker.ts",
 | 
			
		||||
                layer: "sw",
 | 
			
		||||
              }
 | 
			
		||||
            : "./src/entrypoints/service-worker.ts",
 | 
			
		||||
        service_worker: "./src/entrypoints/service_worker.ts",
 | 
			
		||||
        app: "./src/entrypoints/app.ts",
 | 
			
		||||
        authorize: "./src/entrypoints/authorize.ts",
 | 
			
		||||
        onboarding: "./src/entrypoints/onboarding.ts",
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,4 @@ module.exports = {
 | 
			
		||||
    }
 | 
			
		||||
    return version[1];
 | 
			
		||||
  },
 | 
			
		||||
  isDevContainer() {
 | 
			
		||||
    return process.env.DEV_CONTAINER === "1";
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,54 +1,19 @@
 | 
			
		||||
// Tasks to compress
 | 
			
		||||
 | 
			
		||||
import { constants } from "node:zlib";
 | 
			
		||||
import gulp from "gulp";
 | 
			
		||||
import brotli from "gulp-brotli";
 | 
			
		||||
import zopfli from "gulp-zopfli-green";
 | 
			
		||||
import paths from "../paths.cjs";
 | 
			
		||||
 | 
			
		||||
const filesGlob = "*.{js,json,css,svg,xml}";
 | 
			
		||||
const brotliOptions = {
 | 
			
		||||
  skipLarger: true,
 | 
			
		||||
  params: {
 | 
			
		||||
    [constants.BROTLI_PARAM_QUALITY]: constants.BROTLI_MAX_QUALITY,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
const zopfliOptions = { threshold: 150 };
 | 
			
		||||
 | 
			
		||||
const compressDistBrotli = (rootDir, modernDir) =>
 | 
			
		||||
const compressDist = (rootDir) =>
 | 
			
		||||
  gulp
 | 
			
		||||
    .src([`${modernDir}/**/${filesGlob}`, `${rootDir}/sw-modern.js`], {
 | 
			
		||||
      base: rootDir,
 | 
			
		||||
    })
 | 
			
		||||
    .pipe(brotli(brotliOptions))
 | 
			
		||||
    .pipe(gulp.dest(rootDir));
 | 
			
		||||
 | 
			
		||||
const compressDistZopfli = (rootDir, modernDir) =>
 | 
			
		||||
  gulp
 | 
			
		||||
    .src(
 | 
			
		||||
      [
 | 
			
		||||
        `${rootDir}/**/${filesGlob}`,
 | 
			
		||||
        `!${modernDir}/**/${filesGlob}`,
 | 
			
		||||
        `!${rootDir}/{sw-modern,service_worker}.js`,
 | 
			
		||||
        `${rootDir}/{authorize,onboarding}.html`,
 | 
			
		||||
      ],
 | 
			
		||||
      { base: rootDir }
 | 
			
		||||
    )
 | 
			
		||||
    .src([
 | 
			
		||||
      `${rootDir}/**/*.{js,json,css,svg,xml}`,
 | 
			
		||||
      `${rootDir}/{authorize,onboarding}.html`,
 | 
			
		||||
    ])
 | 
			
		||||
    .pipe(zopfli(zopfliOptions))
 | 
			
		||||
    .pipe(gulp.dest(rootDir));
 | 
			
		||||
 | 
			
		||||
const compressAppBrotli = () =>
 | 
			
		||||
  compressDistBrotli(paths.app_output_root, paths.app_output_latest);
 | 
			
		||||
const compressHassioBrotli = () =>
 | 
			
		||||
  compressDistBrotli(paths.hassio_output_root, paths.hassio_output_latest);
 | 
			
		||||
 | 
			
		||||
const compressAppZopfli = () =>
 | 
			
		||||
  compressDistZopfli(paths.app_output_root, paths.app_output_latest);
 | 
			
		||||
const compressHassioZopfli = () =>
 | 
			
		||||
  compressDistZopfli(paths.hassio_output_root, paths.hassio_output_latest);
 | 
			
		||||
 | 
			
		||||
gulp.task("compress-app", gulp.parallel(compressAppBrotli, compressAppZopfli));
 | 
			
		||||
gulp.task(
 | 
			
		||||
  "compress-hassio",
 | 
			
		||||
  gulp.parallel(compressHassioBrotli, compressHassioZopfli)
 | 
			
		||||
);
 | 
			
		||||
gulp.task("compress-app", () => compressDist(paths.app_output_root));
 | 
			
		||||
gulp.task("compress-hassio", () => compressDist(paths.hassio_output_root));
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
// Tasks to generate entry HTML
 | 
			
		||||
 | 
			
		||||
import { getUserAgentRegex } from "browserslist-useragent-regexp";
 | 
			
		||||
import fs from "fs-extra";
 | 
			
		||||
import gulp from "gulp";
 | 
			
		||||
import { minify } from "html-minifier-terser";
 | 
			
		||||
@@ -18,12 +17,6 @@ const renderTemplate = (templateFile, data = {}) => {
 | 
			
		||||
    ...data,
 | 
			
		||||
    useRollup: env.useRollup(),
 | 
			
		||||
    useWDS: env.useWDS(),
 | 
			
		||||
    modernRegex: getUserAgentRegex({
 | 
			
		||||
      env: "modern",
 | 
			
		||||
      allowHigherVersions: true,
 | 
			
		||||
      mobileToDesktop: true,
 | 
			
		||||
      throwOnMissing: true,
 | 
			
		||||
    }).toString(),
 | 
			
		||||
    // Resolve any child/nested templates relative to the parent and pass the same data
 | 
			
		||||
    renderTemplate: (childTemplate) =>
 | 
			
		||||
      renderTemplate(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,20 @@
 | 
			
		||||
// Generate service workers
 | 
			
		||||
// Generate service worker.
 | 
			
		||||
// Based on manifest, create a file with the content as service_worker.js
 | 
			
		||||
 | 
			
		||||
import { deleteAsync } from "del";
 | 
			
		||||
import fs from "fs-extra";
 | 
			
		||||
import gulp from "gulp";
 | 
			
		||||
import { mkdir, readFile, symlink, writeFile } from "node:fs/promises";
 | 
			
		||||
import { basename, join, relative } from "node:path";
 | 
			
		||||
import { injectManifest } from "workbox-build";
 | 
			
		||||
import path from "path";
 | 
			
		||||
import sourceMapUrl from "source-map-url";
 | 
			
		||||
import workboxBuild from "workbox-build";
 | 
			
		||||
import paths from "../paths.cjs";
 | 
			
		||||
 | 
			
		||||
const SW_MAP = {
 | 
			
		||||
  [paths.app_output_latest]: "modern",
 | 
			
		||||
  [paths.app_output_es5]: "legacy",
 | 
			
		||||
};
 | 
			
		||||
const swDest = path.resolve(paths.app_output_root, "service_worker.js");
 | 
			
		||||
 | 
			
		||||
const SW_DEV =
 | 
			
		||||
  `
 | 
			
		||||
const writeSW = (content) => fs.outputFileSync(swDest, content.trim() + "\n");
 | 
			
		||||
 | 
			
		||||
gulp.task("gen-service-worker-app-dev", (done) => {
 | 
			
		||||
  writeSW(
 | 
			
		||||
    `
 | 
			
		||||
console.debug('Service worker disabled in development');
 | 
			
		||||
 | 
			
		||||
self.addEventListener('install', (event) => {
 | 
			
		||||
@@ -21,67 +22,72 @@ self.addEventListener('install', (event) => {
 | 
			
		||||
  // removing any prod service worker the dev might have running
 | 
			
		||||
  self.skipWaiting();
 | 
			
		||||
});
 | 
			
		||||
  `.trim() + "\n";
 | 
			
		||||
 | 
			
		||||
gulp.task("gen-service-worker-app-dev", async () => {
 | 
			
		||||
  await mkdir(paths.app_output_root, { recursive: true });
 | 
			
		||||
  await Promise.all(
 | 
			
		||||
    Object.values(SW_MAP).map((build) =>
 | 
			
		||||
      writeFile(join(paths.app_output_root, `sw-${build}.js`), SW_DEV, {
 | 
			
		||||
        encoding: "utf-8",
 | 
			
		||||
      })
 | 
			
		||||
    )
 | 
			
		||||
  `
 | 
			
		||||
  );
 | 
			
		||||
  done();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
gulp.task("gen-service-worker-app-prod", () =>
 | 
			
		||||
  Promise.all(
 | 
			
		||||
    Object.entries(SW_MAP).map(async ([outPath, build]) => {
 | 
			
		||||
      const manifest = JSON.parse(
 | 
			
		||||
        await readFile(join(outPath, "manifest.json"), "utf-8")
 | 
			
		||||
      );
 | 
			
		||||
      const swSrc = join(paths.app_output_root, manifest["service-worker.js"]);
 | 
			
		||||
      const swDest = join(paths.app_output_root, `sw-${build}.js`);
 | 
			
		||||
      const buildDir = relative(paths.app_output_root, outPath);
 | 
			
		||||
      const { warnings } = await injectManifest({
 | 
			
		||||
        swSrc,
 | 
			
		||||
        swDest,
 | 
			
		||||
        injectionPoint: "__WB_MANIFEST__",
 | 
			
		||||
        // Files that mach this pattern will be considered unique and skip revision check
 | 
			
		||||
        // ignore JS files + translation files
 | 
			
		||||
        dontCacheBustURLsMatching: new RegExp(
 | 
			
		||||
          `(?:${buildDir}/.+|static/translations/.+)`
 | 
			
		||||
        ),
 | 
			
		||||
        globDirectory: paths.app_output_root,
 | 
			
		||||
        globPatterns: [
 | 
			
		||||
          `${buildDir}/*.js`,
 | 
			
		||||
          // Cache all English translations because we catch them as fallback
 | 
			
		||||
          // Using pattern to match hash instead of * to avoid caching en-GB
 | 
			
		||||
          // 'v' added as valid hash letter because in dev we hash with 'dev'
 | 
			
		||||
          "static/translations/**/en-+([a-fv0-9]).json",
 | 
			
		||||
          // Icon shown on splash screen
 | 
			
		||||
          "static/icons/favicon-192x192.png",
 | 
			
		||||
          "static/icons/favicon.ico",
 | 
			
		||||
          // Common fonts
 | 
			
		||||
          "static/fonts/roboto/Roboto-Light.woff2",
 | 
			
		||||
          "static/fonts/roboto/Roboto-Medium.woff2",
 | 
			
		||||
          "static/fonts/roboto/Roboto-Regular.woff2",
 | 
			
		||||
          "static/fonts/roboto/Roboto-Bold.woff2",
 | 
			
		||||
        ],
 | 
			
		||||
        globIgnores: [`${buildDir}/service-worker*`],
 | 
			
		||||
      });
 | 
			
		||||
      if (warnings.length > 0) {
 | 
			
		||||
        console.warn(
 | 
			
		||||
          `Problems while injecting ${build} service worker:\n`,
 | 
			
		||||
          warnings.join("\n")
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
      await deleteAsync(`${swSrc}?(.map)`);
 | 
			
		||||
      // Needed to install new SW from a cached HTML
 | 
			
		||||
      if (build === "modern") {
 | 
			
		||||
        const swOld = join(paths.app_output_root, "service_worker.js");
 | 
			
		||||
        await symlink(basename(swDest), swOld);
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  )
 | 
			
		||||
);
 | 
			
		||||
gulp.task("gen-service-worker-app-prod", async () => {
 | 
			
		||||
  // Read bundled source file
 | 
			
		||||
  const bundleManifestLatest = fs.readJsonSync(
 | 
			
		||||
    path.resolve(paths.app_output_latest, "manifest.json")
 | 
			
		||||
  );
 | 
			
		||||
  let serviceWorkerContent = fs.readFileSync(
 | 
			
		||||
    paths.app_output_root + bundleManifestLatest["service_worker.js"],
 | 
			
		||||
    "utf-8"
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  // Delete old file from frontend_latest so manifest won't pick it up
 | 
			
		||||
  fs.removeSync(
 | 
			
		||||
    paths.app_output_root + bundleManifestLatest["service_worker.js"]
 | 
			
		||||
  );
 | 
			
		||||
  fs.removeSync(
 | 
			
		||||
    paths.app_output_root + bundleManifestLatest["service_worker.js.map"]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  // Remove ES5
 | 
			
		||||
  const bundleManifestES5 = fs.readJsonSync(
 | 
			
		||||
    path.resolve(paths.app_output_es5, "manifest.json")
 | 
			
		||||
  );
 | 
			
		||||
  fs.removeSync(paths.app_output_root + bundleManifestES5["service_worker.js"]);
 | 
			
		||||
  fs.removeSync(
 | 
			
		||||
    paths.app_output_root + bundleManifestES5["service_worker.js.map"]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const workboxManifest = await workboxBuild.getManifest({
 | 
			
		||||
    // Files that mach this pattern will be considered unique and skip revision check
 | 
			
		||||
    // ignore JS files + translation files
 | 
			
		||||
    dontCacheBustURLsMatching: /(frontend_latest\/.+|static\/translations\/.+)/,
 | 
			
		||||
 | 
			
		||||
    globDirectory: paths.app_output_root,
 | 
			
		||||
    globPatterns: [
 | 
			
		||||
      "frontend_latest/*.js",
 | 
			
		||||
      // Cache all English translations because we catch them as fallback
 | 
			
		||||
      // Using pattern to match hash instead of * to avoid caching en-GB
 | 
			
		||||
      // 'v' added as valid hash letter because in dev we hash with 'dev'
 | 
			
		||||
      "static/translations/**/en-+([a-fv0-9]).json",
 | 
			
		||||
      // Icon shown on splash screen
 | 
			
		||||
      "static/icons/favicon-192x192.png",
 | 
			
		||||
      "static/icons/favicon.ico",
 | 
			
		||||
      // Common fonts
 | 
			
		||||
      "static/fonts/roboto/Roboto-Light.woff2",
 | 
			
		||||
      "static/fonts/roboto/Roboto-Medium.woff2",
 | 
			
		||||
      "static/fonts/roboto/Roboto-Regular.woff2",
 | 
			
		||||
      "static/fonts/roboto/Roboto-Bold.woff2",
 | 
			
		||||
    ],
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  for (const warning of workboxManifest.warnings) {
 | 
			
		||||
    console.warn(warning);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // remove source map and add WB manifest
 | 
			
		||||
  serviceWorkerContent = sourceMapUrl.removeFrom(serviceWorkerContent);
 | 
			
		||||
  serviceWorkerContent = serviceWorkerContent.replace(
 | 
			
		||||
    "WB_MANIFEST",
 | 
			
		||||
    JSON.stringify(workboxManifest.manifestEntries)
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  // Write new file to root
 | 
			
		||||
  fs.writeFileSync(swDest, serviceWorkerContent);
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,12 @@
 | 
			
		||||
/* eslint-disable max-classes-per-file */
 | 
			
		||||
 | 
			
		||||
import { deleteAsync } from "del";
 | 
			
		||||
import { glob } from "glob";
 | 
			
		||||
import gulp from "gulp";
 | 
			
		||||
import merge from "gulp-merge-json";
 | 
			
		||||
import rename from "gulp-rename";
 | 
			
		||||
import merge from "lodash.merge";
 | 
			
		||||
import { createHash } from "node:crypto";
 | 
			
		||||
import { mkdir, readFile } from "node:fs/promises";
 | 
			
		||||
import { basename, join } from "node:path";
 | 
			
		||||
import { PassThrough, Transform } from "node:stream";
 | 
			
		||||
import { Transform } from "node:stream";
 | 
			
		||||
import { finished } from "node:stream/promises";
 | 
			
		||||
import env from "../env.cjs";
 | 
			
		||||
import paths from "../paths.cjs";
 | 
			
		||||
@@ -19,7 +17,6 @@ const inBackendDir = "translations/backend";
 | 
			
		||||
const workDir = "build/translations";
 | 
			
		||||
const outDir = join(workDir, "output");
 | 
			
		||||
const EN_SRC = join(paths.translations_src, "en.json");
 | 
			
		||||
const TEST_LOCALE = "en-x-test";
 | 
			
		||||
 | 
			
		||||
let mergeBackend = false;
 | 
			
		||||
 | 
			
		||||
@@ -57,39 +54,6 @@ class CustomJSON extends Transform {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Transform stream to merge Vinyl JSON files (buffer mode only).
 | 
			
		||||
class MergeJSON extends Transform {
 | 
			
		||||
  _objects = [];
 | 
			
		||||
 | 
			
		||||
  constructor(stem, startObj = {}, reviver = null) {
 | 
			
		||||
    super({ objectMode: true, allowHalfOpen: false });
 | 
			
		||||
    this._stem = stem;
 | 
			
		||||
    this._startObj = structuredClone(startObj);
 | 
			
		||||
    this._reviver = reviver;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async _transform(file, _, callback) {
 | 
			
		||||
    try {
 | 
			
		||||
      this._objects.push(JSON.parse(file.contents.toString(), this._reviver));
 | 
			
		||||
      if (!this._outFile) this._outFile = file.clone({ contents: false });
 | 
			
		||||
      callback(null);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      callback(err);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async _flush(callback) {
 | 
			
		||||
    try {
 | 
			
		||||
      const mergedObj = merge(this._startObj, ...this._objects);
 | 
			
		||||
      this._outFile.contents = Buffer.from(JSON.stringify(mergedObj));
 | 
			
		||||
      this._outFile.stem = this._stem;
 | 
			
		||||
      callback(null, this._outFile);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      callback(err);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Utility to flatten object keys to single level using separator
 | 
			
		||||
const flatten = (data, prefix = "", sep = ".") => {
 | 
			
		||||
  const output = {};
 | 
			
		||||
@@ -151,7 +115,7 @@ const createTestTranslation = () =>
 | 
			
		||||
    : gulp
 | 
			
		||||
        .src(EN_SRC)
 | 
			
		||||
        .pipe(new CustomJSON(null, testReviver))
 | 
			
		||||
        .pipe(rename(`${TEST_LOCALE}.json`))
 | 
			
		||||
        .pipe(rename("test.json"))
 | 
			
		||||
        .pipe(gulp.dest(workDir));
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -167,7 +131,12 @@ const createMasterTranslation = () =>
 | 
			
		||||
  gulp
 | 
			
		||||
    .src([EN_SRC, ...(mergeBackend ? [`${inBackendDir}/en.json`] : [])])
 | 
			
		||||
    .pipe(new CustomJSON(lokaliseTransform))
 | 
			
		||||
    .pipe(new MergeJSON("en"))
 | 
			
		||||
    .pipe(
 | 
			
		||||
      merge({
 | 
			
		||||
        fileName: "en.json",
 | 
			
		||||
        jsonSpace: undefined,
 | 
			
		||||
      })
 | 
			
		||||
    )
 | 
			
		||||
    .pipe(gulp.dest(workDir));
 | 
			
		||||
 | 
			
		||||
const FRAGMENTS = ["base"];
 | 
			
		||||
@@ -193,7 +162,7 @@ const createTranslations = async () => {
 | 
			
		||||
  // each locale, then fragmentizes and flattens the data for final output.
 | 
			
		||||
  const translationFiles = await glob([
 | 
			
		||||
    `${inFrontendDir}/!(en).json`,
 | 
			
		||||
    ...(env.isProdBuild() ? [] : [`${workDir}/${TEST_LOCALE}.json`]),
 | 
			
		||||
    ...(env.isProdBuild() ? [] : [`${workDir}/test.json`]),
 | 
			
		||||
  ]);
 | 
			
		||||
  const hashStream = new Transform({
 | 
			
		||||
    objectMode: true,
 | 
			
		||||
@@ -244,19 +213,16 @@ const createTranslations = async () => {
 | 
			
		||||
  // TODO: This is a naive interpretation of BCP47 that should be improved.
 | 
			
		||||
  //       Will be OK for now as long as we don't have anything more complicated
 | 
			
		||||
  // than a base translation + region.
 | 
			
		||||
  const masterStream = gulp
 | 
			
		||||
    .src(`${workDir}/en.json`)
 | 
			
		||||
    .pipe(new PassThrough({ objectMode: true }));
 | 
			
		||||
  masterStream.pipe(hashStream, { end: false });
 | 
			
		||||
  const mergesFinished = [finished(masterStream)];
 | 
			
		||||
  gulp.src(`${workDir}/en.json`).pipe(hashStream, { end: false });
 | 
			
		||||
  const mergesFinished = [];
 | 
			
		||||
  for (const translationFile of translationFiles) {
 | 
			
		||||
    const locale = basename(translationFile, ".json");
 | 
			
		||||
    const subtags = locale.split("-");
 | 
			
		||||
    const mergeFiles = [];
 | 
			
		||||
    for (let i = 1; i <= subtags.length; i++) {
 | 
			
		||||
      const lang = subtags.slice(0, i).join("-");
 | 
			
		||||
      if (lang === TEST_LOCALE) {
 | 
			
		||||
        mergeFiles.push(`${workDir}/${TEST_LOCALE}.json`);
 | 
			
		||||
      if (lang === "test") {
 | 
			
		||||
        mergeFiles.push(`${workDir}/test.json`);
 | 
			
		||||
      } else if (lang !== "en") {
 | 
			
		||||
        mergeFiles.push(`${inFrontendDir}/${lang}.json`);
 | 
			
		||||
        if (mergeBackend) {
 | 
			
		||||
@@ -264,9 +230,14 @@ const createTranslations = async () => {
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    const mergeStream = gulp
 | 
			
		||||
      .src(mergeFiles, { allowEmpty: true })
 | 
			
		||||
      .pipe(new MergeJSON(locale, enMaster, emptyReviver));
 | 
			
		||||
    const mergeStream = gulp.src(mergeFiles, { allowEmpty: true }).pipe(
 | 
			
		||||
      merge({
 | 
			
		||||
        fileName: `${locale}.json`,
 | 
			
		||||
        startObj: enMaster,
 | 
			
		||||
        jsonReviver: emptyReviver,
 | 
			
		||||
        jsonSpace: undefined,
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
    mergesFinished.push(finished(mergeStream));
 | 
			
		||||
    mergeStream.pipe(hashStream, { end: false });
 | 
			
		||||
  }
 | 
			
		||||
@@ -285,7 +256,7 @@ const writeTranslationMetaData = () =>
 | 
			
		||||
      new CustomJSON((meta) => {
 | 
			
		||||
        // Add the test translation in development.
 | 
			
		||||
        if (!env.isProdBuild()) {
 | 
			
		||||
          meta[TEST_LOCALE] = { nativeName: "Translation Test" };
 | 
			
		||||
          meta.test = { nativeName: "Test" };
 | 
			
		||||
        }
 | 
			
		||||
        // Filter out locales without a native name, and add the hashes.
 | 
			
		||||
        for (const locale of Object.keys(meta)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -40,12 +40,8 @@ const runDevServer = async ({
 | 
			
		||||
  compiler,
 | 
			
		||||
  contentBase,
 | 
			
		||||
  port,
 | 
			
		||||
  listenHost = undefined,
 | 
			
		||||
  listenHost = "localhost",
 | 
			
		||||
}) => {
 | 
			
		||||
  if (listenHost === undefined) {
 | 
			
		||||
    // For dev container, we need to listen on all hosts
 | 
			
		||||
    listenHost = env.isDevContainer() ? "0.0.0.0" : "localhost";
 | 
			
		||||
  }
 | 
			
		||||
  const server = new WebpackDevServer(
 | 
			
		||||
    {
 | 
			
		||||
      hot: false,
 | 
			
		||||
 
 | 
			
		||||
@@ -63,25 +63,17 @@ const createWebpackConfig = ({
 | 
			
		||||
      rules: [
 | 
			
		||||
        {
 | 
			
		||||
          test: /\.m?js$|\.ts$/,
 | 
			
		||||
          use: (info) => ({
 | 
			
		||||
          use: {
 | 
			
		||||
            loader: "babel-loader",
 | 
			
		||||
            options: {
 | 
			
		||||
              ...bundle.babelOptions({
 | 
			
		||||
                latestBuild,
 | 
			
		||||
                isProdBuild,
 | 
			
		||||
                isTestBuild,
 | 
			
		||||
                sw: info.issuerLayer === "sw",
 | 
			
		||||
              }),
 | 
			
		||||
              ...bundle.babelOptions({ latestBuild, isProdBuild, isTestBuild }),
 | 
			
		||||
              cacheDirectory: !isProdBuild,
 | 
			
		||||
              cacheCompression: false,
 | 
			
		||||
            },
 | 
			
		||||
          }),
 | 
			
		||||
          },
 | 
			
		||||
          resolve: {
 | 
			
		||||
            fullySpecified: false,
 | 
			
		||||
          },
 | 
			
		||||
          parser: {
 | 
			
		||||
            worker: ["*context.audioWorklet.addModule()", "..."],
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          test: /\.css$/,
 | 
			
		||||
@@ -100,15 +92,11 @@ const createWebpackConfig = ({
 | 
			
		||||
      moduleIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
 | 
			
		||||
      chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
 | 
			
		||||
      splitChunks: {
 | 
			
		||||
        // Disable splitting for web workers and worklets because imports of
 | 
			
		||||
        // external chunks are broken for:
 | 
			
		||||
        // - ESM output: https://github.com/webpack/webpack/issues/17014
 | 
			
		||||
        // - Worklets use `importScripts`: https://github.com/webpack/webpack/issues/11543
 | 
			
		||||
        chunks: (chunk) =>
 | 
			
		||||
          !chunk.canBeInitial() &&
 | 
			
		||||
          !new RegExp(`^.+-work${latestBuild ? "(?:let|er)" : "let"}$`).test(
 | 
			
		||||
            chunk.name
 | 
			
		||||
          ),
 | 
			
		||||
        // Disable splitting for web workers with ESM output
 | 
			
		||||
        // Imports of external chunks are broken
 | 
			
		||||
        chunks: latestBuild
 | 
			
		||||
          ? (chunk) => !chunk.canBeInitial() && !/^.+-worker$/.test(chunk.name)
 | 
			
		||||
          : undefined,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    plugins: [
 | 
			
		||||
@@ -240,7 +228,6 @@ const createWebpackConfig = ({
 | 
			
		||||
      ),
 | 
			
		||||
    },
 | 
			
		||||
    experiments: {
 | 
			
		||||
      layers: true,
 | 
			
		||||
      outputModule: true,
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
self.addEventListener("fetch", (event) => {
 | 
			
		||||
  event.respondWith(fetch(event.request));
 | 
			
		||||
});
 | 
			
		||||
@@ -36,7 +36,13 @@
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <%= renderTemplate("../../../src/html/_js_base.html.template") %>
 | 
			
		||||
    <%= renderTemplate("../../../src/html/_script_loader.html.template") %>
 | 
			
		||||
    <script>
 | 
			
		||||
      <% for (const entry of latestEntryJS) { %>
 | 
			
		||||
        import("<%= entry %>");
 | 
			
		||||
      <% } %>
 | 
			
		||||
      window.latestJS = true;
 | 
			
		||||
    </script>
 | 
			
		||||
    <%= renderTemplate("../../../src/html/_script_load_es5.html.template") %>
 | 
			
		||||
    <hc-layout subtitle="FAQ">
 | 
			
		||||
      <style>
 | 
			
		||||
        a {
 | 
			
		||||
@@ -226,5 +232,17 @@ http:
 | 
			
		||||
        </p>
 | 
			
		||||
      </div>
 | 
			
		||||
    </hc-layout>
 | 
			
		||||
 | 
			
		||||
    <script>
 | 
			
		||||
      var _gaq = [["_setAccount", "UA-57927901-9"], ["_trackPageview"]];
 | 
			
		||||
      (function (d, t) {
 | 
			
		||||
        var g = d.createElement(t),
 | 
			
		||||
          s = d.getElementsByTagName(t)[0];
 | 
			
		||||
        g.src =
 | 
			
		||||
          ("https:" == location.protocol ? "//ssl" : "//www") +
 | 
			
		||||
          ".google-analytics.com/ga.js";
 | 
			
		||||
        s.parentNode.insertBefore(g, s);
 | 
			
		||||
      })(document, "script");
 | 
			
		||||
    </script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
 
 | 
			
		||||
@@ -13,9 +13,15 @@
 | 
			
		||||
    <%= renderTemplate("_social_meta.html.template") %>
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <hc-connect></hc-connect>
 | 
			
		||||
    <%= renderTemplate("../../../src/html/_js_base.html.template") %>
 | 
			
		||||
    <%= renderTemplate("../../../src/html/_script_loader.html.template") %>
 | 
			
		||||
    <hc-connect></hc-connect>
 | 
			
		||||
    <script>
 | 
			
		||||
      <% for (const entry of latestEntryJS) { %>
 | 
			
		||||
        import("<%= entry %>");
 | 
			
		||||
      <% } %>
 | 
			
		||||
      window.latestJS = true;
 | 
			
		||||
    </script>
 | 
			
		||||
    <%= renderTemplate("../../../src/html/_script_load_es5.html.template") %>
 | 
			
		||||
    <script>
 | 
			
		||||
    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
 | 
			
		||||
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
 | 
			
		||||
 
 | 
			
		||||
@@ -14,10 +14,22 @@
 | 
			
		||||
        --background-color: #41bdf5;
 | 
			
		||||
      }
 | 
			
		||||
    </style>
 | 
			
		||||
    <script>
 | 
			
		||||
      var _gaq=[['_setAccount','UA-57927901-10'],['_trackPageview']];
 | 
			
		||||
      (function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
 | 
			
		||||
      g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
 | 
			
		||||
      s.parentNode.insertBefore(g,s)}(document,'script'));
 | 
			
		||||
    </script>
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <cast-media-player></cast-media-player>
 | 
			
		||||
    <%= renderTemplate("../../../src/html/_js_base.html.template") %>
 | 
			
		||||
    <%= renderTemplate("../../../src/html/_script_loader.html.template") %>
 | 
			
		||||
    <cast-media-player></cast-media-player>
 | 
			
		||||
    <script>
 | 
			
		||||
      <% for (const entry of latestEntryJS) { %>
 | 
			
		||||
        import("<%= entry %>");
 | 
			
		||||
      <% } %>
 | 
			
		||||
      window.latestJS = true;
 | 
			
		||||
    </script>
 | 
			
		||||
    <%= renderTemplate("../../../src/html/_script_load_es5.html.template") %>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
 
 | 
			
		||||
@@ -11,4 +11,10 @@
 | 
			
		||||
      font-size: initial;
 | 
			
		||||
    }
 | 
			
		||||
  </style>
 | 
			
		||||
  <script>
 | 
			
		||||
  var _gaq=[['_setAccount','UA-57927901-10'],['_trackPageview']];
 | 
			
		||||
  (function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
 | 
			
		||||
  g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
 | 
			
		||||
  s.parentNode.insertBefore(g,s)}(document,'script'));
 | 
			
		||||
  </script>
 | 
			
		||||
</html>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import "../../../src/resources/safari-14-attachshadow-patch";
 | 
			
		||||
import "./layout/hc-connect";
 | 
			
		||||
 | 
			
		||||
import("../../../src/resources/ha-style");
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import "@material/mwc-button/mwc-button";
 | 
			
		||||
import { ActionDetail } from "@material/mwc-list/mwc-list";
 | 
			
		||||
import { mdiCast, mdiCastConnected, mdiViewDashboard } from "@mdi/js";
 | 
			
		||||
import { mdiCast, mdiCastConnected } from "@mdi/js";
 | 
			
		||||
import "@polymer/paper-item/paper-icon-item";
 | 
			
		||||
import "@polymer/paper-listbox/paper-listbox";
 | 
			
		||||
import { Auth, Connection } from "home-assistant-js-websocket";
 | 
			
		||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
@@ -27,7 +28,6 @@ import { LovelaceViewConfig } from "../../../../src/data/lovelace/config/view";
 | 
			
		||||
import "../../../../src/layouts/hass-loading-screen";
 | 
			
		||||
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
 | 
			
		||||
import "./hc-layout";
 | 
			
		||||
import "../../../../src/components/ha-list-item";
 | 
			
		||||
 | 
			
		||||
@customElement("hc-cast")
 | 
			
		||||
class HcCast extends LitElement {
 | 
			
		||||
@@ -83,37 +83,34 @@ class HcCast extends LitElement {
 | 
			
		||||
              `
 | 
			
		||||
            : html`
 | 
			
		||||
                <div class="section-header">PICK A VIEW</div>
 | 
			
		||||
                <mwc-list @action=${this._handlePickView} activatable>
 | 
			
		||||
                <paper-listbox
 | 
			
		||||
                  attr-for-selected="data-path"
 | 
			
		||||
                  .selected=${this.castManager.status.lovelacePath || ""}
 | 
			
		||||
                >
 | 
			
		||||
                  ${(
 | 
			
		||||
                    this.lovelaceViews ?? [
 | 
			
		||||
                      generateDefaultViewConfig({}, {}, {}, {}, () => ""),
 | 
			
		||||
                    ]
 | 
			
		||||
                  ).map(
 | 
			
		||||
                    (view, idx) =>
 | 
			
		||||
                      html`<ha-list-item
 | 
			
		||||
                        graphic="avatar"
 | 
			
		||||
                        .activated=${this.castManager.status?.lovelacePath ===
 | 
			
		||||
                        (view.path ?? idx)}
 | 
			
		||||
                        .selected=${this.castManager.status?.lovelacePath ===
 | 
			
		||||
                        (view.path ?? idx)}
 | 
			
		||||
                    (view, idx) => html`
 | 
			
		||||
                      <paper-icon-item
 | 
			
		||||
                        @click=${this._handlePickView}
 | 
			
		||||
                        data-path=${view.path || idx}
 | 
			
		||||
                      >
 | 
			
		||||
                        ${view.title || view.path || "Unnamed view"}
 | 
			
		||||
                        ${view.icon
 | 
			
		||||
                          ? html`
 | 
			
		||||
                              <ha-icon
 | 
			
		||||
                                .icon=${view.icon}
 | 
			
		||||
                                slot="graphic"
 | 
			
		||||
                                slot="item-icon"
 | 
			
		||||
                              ></ha-icon>
 | 
			
		||||
                            `
 | 
			
		||||
                          : html`<ha-svg-icon
 | 
			
		||||
                              slot="item-icon"
 | 
			
		||||
                              .path=${mdiViewDashboard}
 | 
			
		||||
                            ></ha-svg-icon>`}</ha-list-item
 | 
			
		||||
                      > `
 | 
			
		||||
                  )}</mwc-list
 | 
			
		||||
                >
 | 
			
		||||
                          : ""}
 | 
			
		||||
                        ${view.title || view.path}
 | 
			
		||||
                      </paper-icon-item>
 | 
			
		||||
                    `
 | 
			
		||||
                  )}
 | 
			
		||||
                </paper-listbox>
 | 
			
		||||
              `}
 | 
			
		||||
 | 
			
		||||
        <div class="card-actions">
 | 
			
		||||
          ${this.castManager.status
 | 
			
		||||
            ? html`
 | 
			
		||||
@@ -185,8 +182,8 @@ class HcCast extends LitElement {
 | 
			
		||||
    this.castManager.requestSession();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _handlePickView(ev: CustomEvent<ActionDetail>) {
 | 
			
		||||
    const path = this.lovelaceViews![ev.detail.index].path ?? ev.detail.index;
 | 
			
		||||
  private async _handlePickView(ev: Event) {
 | 
			
		||||
    const path = (ev.currentTarget as any).getAttribute("data-path");
 | 
			
		||||
    await ensureConnectedCastSession(this.castManager!, this.auth!);
 | 
			
		||||
    castSendShowLovelaceView(this.castManager, this.auth.data.hassUrl, path);
 | 
			
		||||
  }
 | 
			
		||||
@@ -249,14 +246,25 @@ class HcCast extends LitElement {
 | 
			
		||||
        height: 18px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      ha-list-item ha-icon,
 | 
			
		||||
      ha-list-item ha-svg-icon {
 | 
			
		||||
      paper-listbox {
 | 
			
		||||
        padding-top: 0;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      paper-listbox ha-icon {
 | 
			
		||||
        padding: 12px;
 | 
			
		||||
        color: var(--secondary-text-color);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      :host([hide-icons]) ha-icon {
 | 
			
		||||
        display: none;
 | 
			
		||||
      paper-icon-item {
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      paper-icon-item[disabled] {
 | 
			
		||||
        cursor: initial;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      :host([hide-icons]) paper-icon-item {
 | 
			
		||||
        --paper-item-icon-width: 0px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .spacer {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property, query } from "lit/decorators";
 | 
			
		||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
 | 
			
		||||
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
 | 
			
		||||
import { getPanelTitleFromUrlPath } from "../../../../src/data/panel";
 | 
			
		||||
import { Lovelace } from "../../../../src/panels/lovelace/types";
 | 
			
		||||
import "../../../../src/panels/lovelace/views/hui-view";
 | 
			
		||||
import { HomeAssistant } from "../../../../src/types";
 | 
			
		||||
@@ -62,12 +61,7 @@ class HcLovelace extends LitElement {
 | 
			
		||||
      const index = this._viewIndex;
 | 
			
		||||
 | 
			
		||||
      if (index !== undefined) {
 | 
			
		||||
        const title = getPanelTitleFromUrlPath(
 | 
			
		||||
          this.hass,
 | 
			
		||||
          this.urlPath || "lovelace"
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const dashboardTitle = title || this.urlPath;
 | 
			
		||||
        const dashboardTitle = this.lovelaceConfig.title || this.urlPath;
 | 
			
		||||
 | 
			
		||||
        const viewTitle =
 | 
			
		||||
          this.lovelaceConfig.views[index].title ||
 | 
			
		||||
@@ -86,17 +80,10 @@ class HcLovelace extends LitElement {
 | 
			
		||||
          this.lovelaceConfig.views[index].background ||
 | 
			
		||||
          this.lovelaceConfig.background;
 | 
			
		||||
 | 
			
		||||
        const backgroundStyle =
 | 
			
		||||
          typeof configBackground === "string"
 | 
			
		||||
            ? configBackground
 | 
			
		||||
            : configBackground?.image
 | 
			
		||||
              ? `center / cover no-repeat url('${configBackground.image}')`
 | 
			
		||||
              : undefined;
 | 
			
		||||
 | 
			
		||||
        if (backgroundStyle) {
 | 
			
		||||
        if (configBackground) {
 | 
			
		||||
          this._huiView!.style.setProperty(
 | 
			
		||||
            "--lovelace-background",
 | 
			
		||||
            backgroundStyle
 | 
			
		||||
            configBackground
 | 
			
		||||
          );
 | 
			
		||||
        } else {
 | 
			
		||||
          this._huiView!.style.removeProperty("--lovelace-background");
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,6 @@ import { loadLovelaceResources } from "../../../../src/panels/lovelace/common/lo
 | 
			
		||||
import { HassElement } from "../../../../src/state/hass-element";
 | 
			
		||||
import { castContext } from "../cast_context";
 | 
			
		||||
import "./hc-launch-screen";
 | 
			
		||||
import { getPanelTitleFromUrlPath } from "../../../../src/data/panel";
 | 
			
		||||
 | 
			
		||||
const DEFAULT_CONFIG: LovelaceDashboardStrategyConfig = {
 | 
			
		||||
  strategy: {
 | 
			
		||||
@@ -360,11 +359,7 @@ export class HcMain extends HassElement {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleNewLovelaceConfig(lovelaceConfig: LovelaceConfig) {
 | 
			
		||||
    const title = getPanelTitleFromUrlPath(
 | 
			
		||||
      this.hass!,
 | 
			
		||||
      this._urlPath || "lovelace"
 | 
			
		||||
    );
 | 
			
		||||
    castContext.setApplicationState(title || "");
 | 
			
		||||
    castContext.setApplicationState(lovelaceConfig.title || "");
 | 
			
		||||
    this._lovelaceConfig = lovelaceConfig;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 30 KiB  | 
@@ -1,5 +0,0 @@
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
self.addEventListener("fetch", (event) => {
 | 
			
		||||
  event.respondWith(fetch(event.request));
 | 
			
		||||
});
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import { convertEntities } from "../../../../src/fake_data/entity";
 | 
			
		||||
import { DemoConfig } from "../types";
 | 
			
		||||
 | 
			
		||||
export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
 | 
			
		||||
export const demoEntitiesSections: DemoConfig["entities"] = () =>
 | 
			
		||||
  convertEntities({
 | 
			
		||||
    "cover.living_room_garden_shutter": {
 | 
			
		||||
      entity_id: "cover.living_room_garden_shutter",
 | 
			
		||||
@@ -113,30 +113,11 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
 | 
			
		||||
    },
 | 
			
		||||
    "media_player.living_room_nest_mini": {
 | 
			
		||||
      entity_id: "media_player.living_room_nest_mini",
 | 
			
		||||
      state: "on",
 | 
			
		||||
      state: "off",
 | 
			
		||||
      attributes: {
 | 
			
		||||
        device_class: "speaker",
 | 
			
		||||
        volume_level: 0.18,
 | 
			
		||||
        is_volume_muted: false,
 | 
			
		||||
        media_content_type: "music",
 | 
			
		||||
        media_duration: 300,
 | 
			
		||||
        media_position: 0,
 | 
			
		||||
        media_position_updated_at: new Date(
 | 
			
		||||
          // 23 seconds in
 | 
			
		||||
          new Date().getTime() - 23000
 | 
			
		||||
        ).toISOString(),
 | 
			
		||||
        media_title: "I Wasn't Born To Follow",
 | 
			
		||||
        media_artist: "The Byrds",
 | 
			
		||||
        media_album_name: "The Notorious Byrd Brothers",
 | 
			
		||||
        source_list: ["It's A Party", "Radio HSL", "Retro 70s and 80s"],
 | 
			
		||||
        shuffle: false,
 | 
			
		||||
        night_sound: false,
 | 
			
		||||
        speech_enhance: false,
 | 
			
		||||
        friendly_name: localize(
 | 
			
		||||
          "ui.panel.page-demo.config.sections.entities.media_player.living_room_nest_mini"
 | 
			
		||||
        ),
 | 
			
		||||
        entity_picture: "/assets/sections/images/media_player_family_room.jpg",
 | 
			
		||||
        supported_features: 64063,
 | 
			
		||||
        friendly_name: "Living room Nest Mini",
 | 
			
		||||
        supported_features: 152461,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    "cover.kitchen_shutter": {
 | 
			
		||||
@@ -187,27 +168,8 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
 | 
			
		||||
      state: "on",
 | 
			
		||||
      attributes: {
 | 
			
		||||
        device_class: "speaker",
 | 
			
		||||
        volume_level: 0.18,
 | 
			
		||||
        is_volume_muted: false,
 | 
			
		||||
        media_content_type: "music",
 | 
			
		||||
        media_duration: 300,
 | 
			
		||||
        media_position: 0,
 | 
			
		||||
        media_position_updated_at: new Date(
 | 
			
		||||
          // 23 seconds in
 | 
			
		||||
          new Date().getTime() - 23000
 | 
			
		||||
        ).toISOString(),
 | 
			
		||||
        media_title: "I Wasn't Born To Follow",
 | 
			
		||||
        media_artist: "The Byrds",
 | 
			
		||||
        media_album_name: "The Notorious Byrd Brothers",
 | 
			
		||||
        source_list: ["It's A Party", "Radio HSL", "Retro 70s and 80s"],
 | 
			
		||||
        shuffle: false,
 | 
			
		||||
        night_sound: false,
 | 
			
		||||
        speech_enhance: false,
 | 
			
		||||
        friendly_name: localize(
 | 
			
		||||
          "ui.panel.page-demo.config.sections.entities.media_player.kitchen_nest_audio"
 | 
			
		||||
        ),
 | 
			
		||||
        entity_picture: "/assets/sections/images/media_player_family_room.jpg",
 | 
			
		||||
        supported_features: 64063,
 | 
			
		||||
        friendly_name: "Kitchen Nest Audio",
 | 
			
		||||
        supported_features: 152461,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    "binary_sensor.tesla_wall_connector_vehicle_connected": {
 | 
			
		||||
@@ -371,28 +333,8 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
 | 
			
		||||
      entity_id: "media_player.study_nest_hub",
 | 
			
		||||
      state: "off",
 | 
			
		||||
      attributes: {
 | 
			
		||||
        device_class: "speaker",
 | 
			
		||||
        volume_level: 0.18,
 | 
			
		||||
        is_volume_muted: false,
 | 
			
		||||
        media_content_type: "music",
 | 
			
		||||
        media_duration: 300,
 | 
			
		||||
        media_position: 0,
 | 
			
		||||
        media_position_updated_at: new Date(
 | 
			
		||||
          // 23 seconds in
 | 
			
		||||
          new Date().getTime() - 23000
 | 
			
		||||
        ).toISOString(),
 | 
			
		||||
        media_title: "I Wasn't Born To Follow",
 | 
			
		||||
        media_artist: "The Byrds",
 | 
			
		||||
        media_album_name: "The Notorious Byrd Brothers",
 | 
			
		||||
        source_list: ["It's A Party", "Radio HSL", "Retro 70s and 80s"],
 | 
			
		||||
        shuffle: false,
 | 
			
		||||
        night_sound: false,
 | 
			
		||||
        speech_enhance: false,
 | 
			
		||||
        friendly_name: localize(
 | 
			
		||||
          "ui.panel.page-demo.config.sections.entities.media_player.study_nest_hub"
 | 
			
		||||
        ),
 | 
			
		||||
        entity_picture: "/assets/sections/images/media_player_family_room.jpg",
 | 
			
		||||
        supported_features: 64063,
 | 
			
		||||
        friendly_name: "Study Nest Hub",
 | 
			
		||||
        supported_features: 152461,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    "sensor.standing_desk_height": {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,25 +1,40 @@
 | 
			
		||||
import { isFrontpageEmbed } from "../../util/is_frontpage";
 | 
			
		||||
import { DemoConfig } from "../types";
 | 
			
		||||
 | 
			
		||||
export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({
 | 
			
		||||
export const demoLovelaceSections: DemoConfig["lovelace"] = () => ({
 | 
			
		||||
  title: "Home Assistant Demo",
 | 
			
		||||
  views: [
 | 
			
		||||
    {
 | 
			
		||||
      type: "sections",
 | 
			
		||||
      title: isFrontpageEmbed ? "Home Assistant" : "Demo",
 | 
			
		||||
      title: "Demo",
 | 
			
		||||
      path: "home",
 | 
			
		||||
      icon: "mdi:home-assistant",
 | 
			
		||||
      sections: [
 | 
			
		||||
        ...(isFrontpageEmbed
 | 
			
		||||
          ? []
 | 
			
		||||
          : [
 | 
			
		||||
              {
 | 
			
		||||
                title: `${localize("ui.panel.page-demo.config.sections.titles.welcome")} 👋`,
 | 
			
		||||
                cards: [{ type: "custom:ha-demo-card" }],
 | 
			
		||||
              },
 | 
			
		||||
            ]),
 | 
			
		||||
        {
 | 
			
		||||
          title: "Welcome 👋",
 | 
			
		||||
          cards: [{ type: "custom:ha-demo-card" }],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          cards: [
 | 
			
		||||
            {
 | 
			
		||||
              type: "tile",
 | 
			
		||||
              entity: "cover.living_room_garden_shutter",
 | 
			
		||||
              name: "Garden",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              type: "tile",
 | 
			
		||||
              entity: "cover.living_room_graveyard_shutter",
 | 
			
		||||
              name: "Rear",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              type: "tile",
 | 
			
		||||
              entity: "cover.living_room_left_shutter",
 | 
			
		||||
              name: "Left",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              type: "tile",
 | 
			
		||||
              entity: "cover.living_room_right_shutter",
 | 
			
		||||
              name: "Right",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              type: "tile",
 | 
			
		||||
              entity: "light.floor_lamp",
 | 
			
		||||
@@ -45,17 +60,13 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({
 | 
			
		||||
              detail: 1,
 | 
			
		||||
              name: "Temperature",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              type: "tile",
 | 
			
		||||
              entity: "cover.living_room_garden_shutter",
 | 
			
		||||
              name: "Blinds",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              type: "tile",
 | 
			
		||||
              entity: "media_player.living_room_nest_mini",
 | 
			
		||||
              name: "Nest Mini",
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
          title: `🛋️ ${localize("ui.panel.page-demo.config.sections.titles.living_room")} `,
 | 
			
		||||
          title: "🛋️ Living room ",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          type: "grid",
 | 
			
		||||
@@ -88,9 +99,10 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({
 | 
			
		||||
            {
 | 
			
		||||
              type: "tile",
 | 
			
		||||
              entity: "media_player.kitchen_nest_audio",
 | 
			
		||||
              name: "Nest Audio",
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
          title: `👩🍳 ${localize("ui.panel.page-demo.config.sections.titles.kitchen")}`,
 | 
			
		||||
          title: "👩🍳 Kitchen",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          type: "grid",
 | 
			
		||||
@@ -132,7 +144,7 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({
 | 
			
		||||
              color: "dark-grey",
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
          title: `⚡️ ${localize("ui.panel.page-demo.config.sections.titles.energy")}`,
 | 
			
		||||
          title: "⚡️ Energy",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          type: "grid",
 | 
			
		||||
@@ -169,7 +181,7 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({
 | 
			
		||||
              state_content: ["preset_mode", "current_temperature"],
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
          title: `🌤️ ${localize("ui.panel.page-demo.config.sections.titles.climate")}`,
 | 
			
		||||
          title: "🌤️ Climate",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          type: "grid",
 | 
			
		||||
@@ -187,6 +199,7 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({
 | 
			
		||||
            {
 | 
			
		||||
              type: "tile",
 | 
			
		||||
              entity: "media_player.study_nest_hub",
 | 
			
		||||
              name: "Nest Hub",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              type: "tile",
 | 
			
		||||
@@ -196,7 +209,7 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({
 | 
			
		||||
              icon: "mdi:desk",
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
          title: `🧑💻 ${localize("ui.panel.page-demo.config.sections.titles.study")}`,
 | 
			
		||||
          title: "🧑💻 Study",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          type: "grid",
 | 
			
		||||
@@ -230,7 +243,7 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({
 | 
			
		||||
              name: "Illuminance",
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
          title: `🌳 ${localize("ui.panel.page-demo.config.sections.titles.outdoor")}`,
 | 
			
		||||
          title: "🌳 Outdoor",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          type: "grid",
 | 
			
		||||
@@ -260,7 +273,7 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({
 | 
			
		||||
              icon: "mdi:home-assistant",
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
          title: `🎉 ${localize("ui.panel.page-demo.config.sections.titles.updates")}`,
 | 
			
		||||
          title: "🎉 Updates",
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,8 @@
 | 
			
		||||
import "@material/mwc-button";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { until } from "lit/directives/until";
 | 
			
		||||
import { fireEvent } from "../../../src/common/dom/fire_event";
 | 
			
		||||
import "../../../src/components/ha-card";
 | 
			
		||||
import "../../../src/components/ha-button";
 | 
			
		||||
import "../../../src/components/ha-circular-progress";
 | 
			
		||||
import { LovelaceCardConfig } from "../../../src/data/lovelace/config/card";
 | 
			
		||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
@@ -12,6 +11,7 @@ import {
 | 
			
		||||
  demoConfigs,
 | 
			
		||||
  selectedDemoConfig,
 | 
			
		||||
  selectedDemoConfigIndex,
 | 
			
		||||
  setDemoConfig,
 | 
			
		||||
} from "../configs/demo-configs";
 | 
			
		||||
 | 
			
		||||
@customElement("ha-demo-card")
 | 
			
		||||
@@ -64,9 +64,9 @@ export class HADemoCard extends LitElement implements LovelaceCard {
 | 
			
		||||
                )}
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <ha-button @click=${this._nextConfig} .disabled=${this._switching}>
 | 
			
		||||
          <mwc-button @click=${this._nextConfig} .disabled=${this._switching}>
 | 
			
		||||
            ${this.hass.localize("ui.panel.page-demo.cards.demo.next_demo")}
 | 
			
		||||
          </ha-button>
 | 
			
		||||
          </mwc-button>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="content">
 | 
			
		||||
          <p class="small-hidden">
 | 
			
		||||
@@ -87,9 +87,9 @@ export class HADemoCard extends LitElement implements LovelaceCard {
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="actions small-hidden">
 | 
			
		||||
          <a href="https://www.home-assistant.io" target="_blank">
 | 
			
		||||
            <ha-button>
 | 
			
		||||
            <mwc-button>
 | 
			
		||||
              ${this.hass.localize("ui.panel.page-demo.cards.demo.learn_more")}
 | 
			
		||||
            </ha-button>
 | 
			
		||||
            </mwc-button>
 | 
			
		||||
          </a>
 | 
			
		||||
        </div>
 | 
			
		||||
      </ha-card>
 | 
			
		||||
@@ -113,7 +113,13 @@ export class HADemoCard extends LitElement implements LovelaceCard {
 | 
			
		||||
 | 
			
		||||
  private async _updateConfig(index: number) {
 | 
			
		||||
    this._switching = true;
 | 
			
		||||
    fireEvent(this, "set-demo-config" as any, { index });
 | 
			
		||||
    try {
 | 
			
		||||
      await setDemoConfig(this.hass, this.lovelace!, index);
 | 
			
		||||
    } catch (err: any) {
 | 
			
		||||
      alert("Failed to switch config :-(");
 | 
			
		||||
    } finally {
 | 
			
		||||
      this._switching = false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
@@ -143,7 +149,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
 | 
			
		||||
          height: 60px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .picker ha-button {
 | 
			
		||||
        .picker mwc-button {
 | 
			
		||||
          margin-right: 8px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import "./util/is_frontpage";
 | 
			
		||||
import "../../src/resources/safari-14-attachshadow-patch";
 | 
			
		||||
import "./ha-demo";
 | 
			
		||||
 | 
			
		||||
import("../../src/resources/ha-style");
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ import {
 | 
			
		||||
import { HomeAssistantAppEl } from "../../src/layouts/home-assistant";
 | 
			
		||||
import { HomeAssistant } from "../../src/types";
 | 
			
		||||
import { selectedDemoConfig } from "./configs/demo-configs";
 | 
			
		||||
import { mockAreaRegistry } from "./stubs/area_registry";
 | 
			
		||||
import { mockAuth } from "./stubs/auth";
 | 
			
		||||
import { mockConfigEntries } from "./stubs/config_entries";
 | 
			
		||||
import { mockEnergy } from "./stubs/energy";
 | 
			
		||||
@@ -24,10 +23,10 @@ import { mockLovelace } from "./stubs/lovelace";
 | 
			
		||||
import { mockMediaPlayer } from "./stubs/media_player";
 | 
			
		||||
import { mockPersistentNotification } from "./stubs/persistent_notification";
 | 
			
		||||
import { mockRecorder } from "./stubs/recorder";
 | 
			
		||||
import { mockTodo } from "./stubs/todo";
 | 
			
		||||
import { mockSensor } from "./stubs/sensor";
 | 
			
		||||
import { mockSystemLog } from "./stubs/system_log";
 | 
			
		||||
import { mockTemplate } from "./stubs/template";
 | 
			
		||||
import { mockTodo } from "./stubs/todo";
 | 
			
		||||
import { mockTranslations } from "./stubs/translations";
 | 
			
		||||
 | 
			
		||||
@customElement("ha-demo")
 | 
			
		||||
@@ -63,7 +62,6 @@ export class HaDemo extends HomeAssistantAppEl {
 | 
			
		||||
    mockEnergy(hass);
 | 
			
		||||
    mockPersistentNotification(hass);
 | 
			
		||||
    mockConfigEntries(hass);
 | 
			
		||||
    mockAreaRegistry(hass);
 | 
			
		||||
    mockEntityRegistry(hass, [
 | 
			
		||||
      {
 | 
			
		||||
        config_entry_id: "co2signal",
 | 
			
		||||
@@ -82,8 +80,6 @@ export class HaDemo extends HomeAssistantAppEl {
 | 
			
		||||
        has_entity_name: false,
 | 
			
		||||
        unique_id: "co2_intensity",
 | 
			
		||||
        options: null,
 | 
			
		||||
        created_at: 0,
 | 
			
		||||
        modified_at: 0,
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        config_entry_id: "co2signal",
 | 
			
		||||
@@ -102,8 +98,6 @@ export class HaDemo extends HomeAssistantAppEl {
 | 
			
		||||
        has_entity_name: false,
 | 
			
		||||
        unique_id: "grid_fossil_fuel_percentage",
 | 
			
		||||
        options: null,
 | 
			
		||||
        created_at: 0,
 | 
			
		||||
        modified_at: 0,
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -63,47 +63,46 @@
 | 
			
		||||
        align-items: center;
 | 
			
		||||
      }
 | 
			
		||||
      #ha-launch-screen svg {
 | 
			
		||||
        width: 112px;
 | 
			
		||||
        width: 170px;
 | 
			
		||||
        flex-shrink: 0;
 | 
			
		||||
      }
 | 
			
		||||
      #ha-launch-screen .ha-launch-screen-spacer-top {
 | 
			
		||||
      #ha-launch-screen .ha-launch-screen-spacer {
 | 
			
		||||
        flex: 1;
 | 
			
		||||
        margin-top: calc( 2 * max(env(safe-area-inset-bottom), 48px) + 46px );
 | 
			
		||||
        padding-top: 48px;
 | 
			
		||||
      }
 | 
			
		||||
      #ha-launch-screen .ha-launch-screen-spacer-bottom {
 | 
			
		||||
        flex: 1;
 | 
			
		||||
        padding-top: 48px;
 | 
			
		||||
      }
 | 
			
		||||
      .ohf-logo {
 | 
			
		||||
        margin: max(env(safe-area-inset-bottom), 48px) 0;
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
        opacity: .66;
 | 
			
		||||
      }
 | 
			
		||||
      @media (prefers-color-scheme: dark) {
 | 
			
		||||
        .ohf-logo {
 | 
			
		||||
          filter: invert(1);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    </style>
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <div id="ha-launch-screen">
 | 
			
		||||
      <div class="ha-launch-screen-spacer-top"></div>
 | 
			
		||||
      <div class="ha-launch-screen-spacer"></div>
 | 
			
		||||
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
 | 
			
		||||
        <path fill="#18BCF2" d="M240 224.762a15 15 0 0 1-15 15H15a15 15 0 0 1-15-15v-90c0-8.25 4.77-19.769 10.61-25.609l98.78-98.7805c5.83-5.83 15.38-5.83 21.21 0l98.79 98.7895c5.83 5.83 10.61 17.36 10.61 25.61v90-.01Z"/>
 | 
			
		||||
        <path fill="#F2F4F9" d="m107.27 239.762-40.63-40.63c-2.09.72-4.32 1.13-6.64 1.13-11.3 0-20.5-9.2-20.5-20.5s9.2-20.5 20.5-20.5 20.5 9.2 20.5 20.5c0 2.33-.41 4.56-1.13 6.65l31.63 31.63v-115.88c-6.8-3.3395-11.5-10.3195-11.5-18.3895 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5c0 8.07-4.7 15.05-11.5 18.3895v81.27l31.46-31.46c-.62-1.96-.96-4.04-.96-6.2 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5-9.2 20.5-20.5 20.5c-2.5 0-4.88-.47-7.09-1.29L129 208.892v30.88z"/>
 | 
			
		||||
      </svg>
 | 
			
		||||
      <div id="ha-launch-screen-info-box" class="ha-launch-screen-spacer-bottom"></div>
 | 
			
		||||
      <div class="ohf-logo">
 | 
			
		||||
        <img src="/static/images/ohf-badge.svg" alt="Home Assistant is a project by the Open Home Foundation" height="46">
 | 
			
		||||
      </div>
 | 
			
		||||
      <div id="ha-launch-screen-info-box" class="ha-launch-screen-spacer"></div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <ha-demo></ha-demo>
 | 
			
		||||
    <%= renderTemplate("../../../src/html/_js_base.html.template") %>
 | 
			
		||||
    <%= renderTemplate("../../../src/html/_preload_roboto.html.template") %>
 | 
			
		||||
    <%= renderTemplate("../../../src/html/_script_loader.html.template") %>
 | 
			
		||||
    <script>
 | 
			
		||||
      // Safari 12 and below does not have a compliant ES2015 implementation of template literals, so we ship ES5
 | 
			
		||||
      if (!isS11_12) {
 | 
			
		||||
        <% for (const entry of latestEntryJS) { %>
 | 
			
		||||
          import("<%= entry %>");
 | 
			
		||||
        <% } %>
 | 
			
		||||
        window.latestJS = true;
 | 
			
		||||
      }
 | 
			
		||||
    </script>
 | 
			
		||||
    <%= renderTemplate("../../../src/html/_script_load_es5.html.template") %>
 | 
			
		||||
    <script>
 | 
			
		||||
      var _gaq = [["_setAccount", "UA-57927901-5"], ["_trackPageview"]];
 | 
			
		||||
      (function (d, t) {
 | 
			
		||||
        var g = d.createElement(t),
 | 
			
		||||
          s = d.getElementsByTagName(t)[0];
 | 
			
		||||
        g.src =
 | 
			
		||||
          ("https:" == location.protocol ? "//ssl" : "//www") +
 | 
			
		||||
          ".google-analytics.com/ga.js";
 | 
			
		||||
        s.parentNode.insertBefore(g, s);
 | 
			
		||||
      })(document, "script");
 | 
			
		||||
    </script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,55 +1,5 @@
 | 
			
		||||
import { convertEntities } from "../../../src/fake_data/entity";
 | 
			
		||||
 | 
			
		||||
export const mapEntities = () =>
 | 
			
		||||
  convertEntities({
 | 
			
		||||
    "zone.home": {
 | 
			
		||||
      entity_id: "zone.home",
 | 
			
		||||
      state: "zoning",
 | 
			
		||||
      attributes: {
 | 
			
		||||
        hidden: true,
 | 
			
		||||
        latitude: 52.3631339,
 | 
			
		||||
        longitude: 4.8903147,
 | 
			
		||||
        radius: 200,
 | 
			
		||||
        friendly_name: "Home",
 | 
			
		||||
        icon: "hademo:home",
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    "zone.uva": {
 | 
			
		||||
      entity_id: "zone.buckhead",
 | 
			
		||||
      state: "zoning",
 | 
			
		||||
      attributes: {
 | 
			
		||||
        hidden: true,
 | 
			
		||||
        radius: 400,
 | 
			
		||||
        friendly_name: "UvA",
 | 
			
		||||
        icon: "hademo:school",
 | 
			
		||||
        latitude: 52.3558182,
 | 
			
		||||
        longitude: 4.9535376,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    "person.arsaboo": {
 | 
			
		||||
      entity_id: "person.arsaboo",
 | 
			
		||||
      state: "not_home",
 | 
			
		||||
      attributes: {
 | 
			
		||||
        radius: 50,
 | 
			
		||||
        friendly_name: "Arsaboo",
 | 
			
		||||
        latitude: 52.3579946,
 | 
			
		||||
        longitude: 4.8664597,
 | 
			
		||||
        entity_picture: "/assets/arsaboo/images/arsaboo.jpg",
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    "person.melody": {
 | 
			
		||||
      entity_id: "person.melody",
 | 
			
		||||
      state: "not_home",
 | 
			
		||||
      attributes: {
 | 
			
		||||
        radius: 50,
 | 
			
		||||
        friendly_name: "Melody",
 | 
			
		||||
        latitude: 52.3408927,
 | 
			
		||||
        longitude: 4.8711073,
 | 
			
		||||
        entity_picture: "/assets/arsaboo/images/melody.jpg",
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
export const energyEntities = () =>
 | 
			
		||||
  convertEntities({
 | 
			
		||||
    "sensor.grid_fossil_fuel_percentage": {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,52 +1,35 @@
 | 
			
		||||
import type { LocalizeFunc } from "../../../src/common/translations/localize";
 | 
			
		||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import {
 | 
			
		||||
  selectedDemoConfig,
 | 
			
		||||
  selectedDemoConfigIndex,
 | 
			
		||||
  setDemoConfig,
 | 
			
		||||
} from "../configs/demo-configs";
 | 
			
		||||
import { selectedDemoConfig } from "../configs/demo-configs";
 | 
			
		||||
import "../custom-cards/cast-demo-row";
 | 
			
		||||
import "../custom-cards/ha-demo-card";
 | 
			
		||||
import { mapEntities } from "./entities";
 | 
			
		||||
import type { HADemoCard } from "../custom-cards/ha-demo-card";
 | 
			
		||||
 | 
			
		||||
export const mockLovelace = (
 | 
			
		||||
  hass: MockHomeAssistant,
 | 
			
		||||
  localizePromise: Promise<LocalizeFunc>
 | 
			
		||||
) => {
 | 
			
		||||
  hass.mockWS("lovelace/config", ({ url_path }) => {
 | 
			
		||||
    if (url_path === "map") {
 | 
			
		||||
      hass.addEntities(mapEntities());
 | 
			
		||||
      return {
 | 
			
		||||
        strategy: {
 | 
			
		||||
          type: "map",
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    return Promise.all([selectedDemoConfig, localizePromise]).then(
 | 
			
		||||
  hass.mockWS("lovelace/config", () =>
 | 
			
		||||
    Promise.all([selectedDemoConfig, localizePromise]).then(
 | 
			
		||||
      ([config, localize]) => config.lovelace(localize)
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
    )
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  hass.mockWS("lovelace/config/save", () => Promise.resolve());
 | 
			
		||||
  hass.mockWS("lovelace/resources", () => Promise.resolve([]));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
customElements.whenDefined("hui-root").then(() => {
 | 
			
		||||
customElements.whenDefined("hui-view").then(() => {
 | 
			
		||||
  // eslint-disable-next-line
 | 
			
		||||
  const HUIRoot = customElements.get("hui-root")!;
 | 
			
		||||
  const HUIView = customElements.get("hui-view");
 | 
			
		||||
  // Patch HUI-VIEW to make the lovelace object available to the demo card
 | 
			
		||||
  const oldCreateCard = HUIView!.prototype.createCardElement;
 | 
			
		||||
 | 
			
		||||
  const oldFirstUpdated = HUIRoot.prototype.firstUpdated;
 | 
			
		||||
 | 
			
		||||
  HUIRoot.prototype.firstUpdated = function (changedProperties) {
 | 
			
		||||
    oldFirstUpdated.call(this, changedProperties);
 | 
			
		||||
    this.addEventListener("set-demo-config", async (ev) => {
 | 
			
		||||
      const index = (ev as CustomEvent).detail.index;
 | 
			
		||||
      try {
 | 
			
		||||
        await setDemoConfig(this.hass, this.lovelace!, index);
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        setDemoConfig(this.hass, this.lovelace!, selectedDemoConfigIndex);
 | 
			
		||||
        alert("Failed to switch config :-(");
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  HUIView!.prototype.createCardElement = function (config) {
 | 
			
		||||
    const el = oldCreateCard.call(this, config);
 | 
			
		||||
    if (el.tagName === "HA-DEMO-CARD") {
 | 
			
		||||
      (el as HADemoCard).lovelace = this.lovelace;
 | 
			
		||||
    }
 | 
			
		||||
    return el;
 | 
			
		||||
  };
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
export const isFrontpageEmbed = document.location.search === "?frontpage";
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 110 KiB  | 
@@ -1,9 +1,7 @@
 | 
			
		||||
import { load } from "js-yaml";
 | 
			
		||||
import { LitElement, PropertyValueMap, css, html, nothing } from "lit";
 | 
			
		||||
import { html, css, LitElement, PropertyValues } from "lit";
 | 
			
		||||
import { customElement, property, query, state } from "lit/decorators";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import "../../../src/panels/lovelace/cards/hui-card";
 | 
			
		||||
import type { HuiCard } from "../../../src/panels/lovelace/cards/hui-card";
 | 
			
		||||
import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element";
 | 
			
		||||
import { HomeAssistant } from "../../../src/types";
 | 
			
		||||
 | 
			
		||||
export interface DemoCardConfig {
 | 
			
		||||
@@ -21,12 +19,7 @@ class DemoCard extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @state() private _size?: number;
 | 
			
		||||
 | 
			
		||||
  @query("hui-card", false) private _card?: HuiCard;
 | 
			
		||||
 | 
			
		||||
  private _config = memoizeOne((config: string) => {
 | 
			
		||||
    const c = (load(config) as any)[0];
 | 
			
		||||
    return c;
 | 
			
		||||
  });
 | 
			
		||||
  @query("#card") private _card!: HTMLElement;
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    return html`
 | 
			
		||||
@@ -37,32 +30,63 @@ class DemoCard extends LitElement {
 | 
			
		||||
          : ""}
 | 
			
		||||
      </h2>
 | 
			
		||||
      <div class="root">
 | 
			
		||||
        <hui-card
 | 
			
		||||
          .config=${this._config(this.config.config)}
 | 
			
		||||
          .hass=${this.hass}
 | 
			
		||||
          @card-updated=${this._cardUpdated}
 | 
			
		||||
        ></hui-card>
 | 
			
		||||
        ${this.showConfig
 | 
			
		||||
          ? html`<pre>${this.config.config.trim()}</pre>`
 | 
			
		||||
          : nothing}
 | 
			
		||||
        <div id="card"></div>
 | 
			
		||||
        ${this.showConfig ? html`<pre>${this.config.config.trim()}</pre>` : ""}
 | 
			
		||||
      </div>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _cardUpdated(ev) {
 | 
			
		||||
    ev.stopPropagation();
 | 
			
		||||
    this._updateSize();
 | 
			
		||||
  updated(changedProps: PropertyValues) {
 | 
			
		||||
    super.updated(changedProps);
 | 
			
		||||
 | 
			
		||||
    if (changedProps.has("config")) {
 | 
			
		||||
      const card = this._card;
 | 
			
		||||
      while (card.lastChild) {
 | 
			
		||||
        card.removeChild(card.lastChild);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const el = this._createCardElement((load(this.config.config) as any)[0]);
 | 
			
		||||
      card.appendChild(el);
 | 
			
		||||
      this._getSize(el);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (changedProps.has("hass")) {
 | 
			
		||||
      const card = this._card.lastChild;
 | 
			
		||||
      if (card) {
 | 
			
		||||
        (card as any).hass = this.hass;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _updateSize() {
 | 
			
		||||
    this._size = await this._card?.getCardSize();
 | 
			
		||||
  async _getSize(el) {
 | 
			
		||||
    await customElements.whenDefined(el.localName);
 | 
			
		||||
 | 
			
		||||
    if (!("getCardSize" in el)) {
 | 
			
		||||
      this._size = undefined;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this._size = await el.getCardSize();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected update(
 | 
			
		||||
    _changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>
 | 
			
		||||
  ): void {
 | 
			
		||||
    super.update(_changedProperties);
 | 
			
		||||
    this._updateSize();
 | 
			
		||||
  _createCardElement(cardConfig) {
 | 
			
		||||
    const element = createCardElement(cardConfig);
 | 
			
		||||
    if (this.hass) {
 | 
			
		||||
      element.hass = this.hass;
 | 
			
		||||
    }
 | 
			
		||||
    element.addEventListener(
 | 
			
		||||
      "ll-rebuild",
 | 
			
		||||
      (ev) => {
 | 
			
		||||
        ev.stopPropagation();
 | 
			
		||||
        this._rebuildCard(element, cardConfig);
 | 
			
		||||
      },
 | 
			
		||||
      { once: true }
 | 
			
		||||
    );
 | 
			
		||||
    return element;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _rebuildCard(cardElToReplace, config) {
 | 
			
		||||
    const newCardEl = this._createCardElement(config);
 | 
			
		||||
    cardElToReplace.parentElement.replaceChild(newCardEl, cardElToReplace);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static styles = css`
 | 
			
		||||
@@ -77,7 +101,7 @@ class DemoCard extends LitElement {
 | 
			
		||||
      font-size: 0.5em;
 | 
			
		||||
      color: var(--primary-text-color);
 | 
			
		||||
    }
 | 
			
		||||
    hui-card {
 | 
			
		||||
    #card {
 | 
			
		||||
      max-width: 400px;
 | 
			
		||||
      width: 100vw;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,16 +3,13 @@ title: When to use remove, delete, add and create
 | 
			
		||||
subtitle: The difference between remove/delete and add/create.
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
# Removing or deleting content
 | 
			
		||||
# Remove vs Delete
 | 
			
		||||
 | 
			
		||||
_Remove_ and _Delete_ are quite similar, but can be frustrating if used inconsistently.
 | 
			
		||||
 | 
			
		||||
- Remove refers to an action that can be restored or reapplied.
 | 
			
		||||
- Delete refers to a permanent, non-recoverable action.
 | 
			
		||||
Remove and Delete are quite similar, but can be frustrating if used inconsistently.
 | 
			
		||||
 | 
			
		||||
## Remove
 | 
			
		||||
 | 
			
		||||
The term _Remove_ should always be used when an item/setting or content is to be removed or disassociated, but the action can be reversed or reapplied.
 | 
			
		||||
Take away and set aside, but kept in existence.
 | 
			
		||||
 | 
			
		||||
For example:
 | 
			
		||||
 | 
			
		||||
@@ -25,7 +22,7 @@ For example:
 | 
			
		||||
 | 
			
		||||
## Delete
 | 
			
		||||
 | 
			
		||||
The term _Delete_ should always be used to refer to any action that will cause the permanent deletion of an item/setting or content.
 | 
			
		||||
Erase, rendered nonexistent or nonrecoverable.
 | 
			
		||||
 | 
			
		||||
For example:
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -64,12 +64,6 @@ const ACTIONS = [
 | 
			
		||||
      entity_id: "input_boolean.toggle_4",
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    sequence: [
 | 
			
		||||
      { scene: "scene.kitchen_morning" },
 | 
			
		||||
      { service: "light.turn_off", target: { entity_id: "light.kitchen" } },
 | 
			
		||||
    ],
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    parallel: [
 | 
			
		||||
      { scene: "scene.kitchen_morning" },
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,6 @@ import { HaWaitForTriggerAction } from "../../../../src/panels/config/automation
 | 
			
		||||
import { HaWaitAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-wait_template";
 | 
			
		||||
import { Action } from "../../../../src/data/script";
 | 
			
		||||
import { HaConditionAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-condition";
 | 
			
		||||
import { HaSequenceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-sequence";
 | 
			
		||||
import { HaParallelAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-parallel";
 | 
			
		||||
import { HaIfAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-if";
 | 
			
		||||
import { HaStopAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-stop";
 | 
			
		||||
@@ -40,7 +39,6 @@ const SCHEMAS: { name: string; actions: Action[] }[] = [
 | 
			
		||||
  { name: "If-Then", actions: [HaIfAction.defaultConfig] },
 | 
			
		||||
  { name: "Choose", actions: [HaChooseAction.defaultConfig] },
 | 
			
		||||
  { name: "Variables", actions: [{ variables: { hello: "1" } }] },
 | 
			
		||||
  { name: "Sequence", actions: [HaSequenceAction.defaultConfig] },
 | 
			
		||||
  { name: "Parallel", actions: [HaParallelAction.defaultConfig] },
 | 
			
		||||
  { name: "Stop", actions: [HaStopAction.defaultConfig] },
 | 
			
		||||
];
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,6 @@ import { getEntity } from "../../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
 | 
			
		||||
import { HomeAssistant } from "../../../../src/types";
 | 
			
		||||
import "../../components/demo-black-white-row";
 | 
			
		||||
import { DeviceRegistryEntry } from "../../../../src/data/device_registry";
 | 
			
		||||
 | 
			
		||||
const ENTITIES = [
 | 
			
		||||
  getEntity("alarm_control_panel", "alarm", "disarmed", {
 | 
			
		||||
@@ -42,7 +41,7 @@ const ENTITIES = [
 | 
			
		||||
  }),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const DEVICES: DeviceRegistryEntry[] = [
 | 
			
		||||
const DEVICES = [
 | 
			
		||||
  {
 | 
			
		||||
    area_id: "bedroom",
 | 
			
		||||
    configuration_url: null,
 | 
			
		||||
@@ -54,7 +53,6 @@ const DEVICES: DeviceRegistryEntry[] = [
 | 
			
		||||
    identifiers: [["demo", "volume1"] as [string, string]],
 | 
			
		||||
    manufacturer: null,
 | 
			
		||||
    model: null,
 | 
			
		||||
    model_id: null,
 | 
			
		||||
    name_by_user: null,
 | 
			
		||||
    name: "Dishwasher",
 | 
			
		||||
    sw_version: null,
 | 
			
		||||
@@ -62,8 +60,6 @@ const DEVICES: DeviceRegistryEntry[] = [
 | 
			
		||||
    via_device_id: null,
 | 
			
		||||
    serial_number: null,
 | 
			
		||||
    labels: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    area_id: "backyard",
 | 
			
		||||
@@ -76,7 +72,6 @@ const DEVICES: DeviceRegistryEntry[] = [
 | 
			
		||||
    identifiers: [["demo", "pwm1"] as [string, string]],
 | 
			
		||||
    manufacturer: null,
 | 
			
		||||
    model: null,
 | 
			
		||||
    model_id: null,
 | 
			
		||||
    name_by_user: null,
 | 
			
		||||
    name: "Lamp",
 | 
			
		||||
    sw_version: null,
 | 
			
		||||
@@ -84,8 +79,6 @@ const DEVICES: DeviceRegistryEntry[] = [
 | 
			
		||||
    via_device_id: null,
 | 
			
		||||
    serial_number: null,
 | 
			
		||||
    labels: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    area_id: null,
 | 
			
		||||
@@ -98,7 +91,6 @@ const DEVICES: DeviceRegistryEntry[] = [
 | 
			
		||||
    identifiers: [["demo", "pwm1"] as [string, string]],
 | 
			
		||||
    manufacturer: null,
 | 
			
		||||
    model: null,
 | 
			
		||||
    model_id: null,
 | 
			
		||||
    name_by_user: "User name",
 | 
			
		||||
    name: "Technical name",
 | 
			
		||||
    sw_version: null,
 | 
			
		||||
@@ -106,8 +98,6 @@ const DEVICES: DeviceRegistryEntry[] = [
 | 
			
		||||
    via_device_id: null,
 | 
			
		||||
    serial_number: null,
 | 
			
		||||
    labels: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@@ -120,8 +110,6 @@ const AREAS: AreaRegistryEntry[] = [
 | 
			
		||||
    picture: null,
 | 
			
		||||
    aliases: [],
 | 
			
		||||
    labels: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    area_id: "bedroom",
 | 
			
		||||
@@ -131,8 +119,6 @@ const AREAS: AreaRegistryEntry[] = [
 | 
			
		||||
    picture: null,
 | 
			
		||||
    aliases: [],
 | 
			
		||||
    labels: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    area_id: "livingroom",
 | 
			
		||||
@@ -142,8 +128,6 @@ const AREAS: AreaRegistryEntry[] = [
 | 
			
		||||
    picture: null,
 | 
			
		||||
    aliases: [],
 | 
			
		||||
    labels: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,6 @@ import { FloorRegistryEntry } from "../../../../src/data/floor_registry";
 | 
			
		||||
import { LabelRegistryEntry } from "../../../../src/data/label_registry";
 | 
			
		||||
import { mockFloorRegistry } from "../../../../demo/src/stubs/floor_registry";
 | 
			
		||||
import { mockLabelRegistry } from "../../../../demo/src/stubs/label_registry";
 | 
			
		||||
import { DeviceRegistryEntry } from "../../../../src/data/device_registry";
 | 
			
		||||
 | 
			
		||||
const ENTITIES = [
 | 
			
		||||
  getEntity("alarm_control_panel", "alarm", "disarmed", {
 | 
			
		||||
@@ -42,7 +41,7 @@ const ENTITIES = [
 | 
			
		||||
  }),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const DEVICES: DeviceRegistryEntry[] = [
 | 
			
		||||
const DEVICES = [
 | 
			
		||||
  {
 | 
			
		||||
    area_id: "bedroom",
 | 
			
		||||
    configuration_url: null,
 | 
			
		||||
@@ -54,7 +53,6 @@ const DEVICES: DeviceRegistryEntry[] = [
 | 
			
		||||
    identifiers: [["demo", "volume1"] as [string, string]],
 | 
			
		||||
    manufacturer: null,
 | 
			
		||||
    model: null,
 | 
			
		||||
    model_id: null,
 | 
			
		||||
    name_by_user: null,
 | 
			
		||||
    name: "Dishwasher",
 | 
			
		||||
    sw_version: null,
 | 
			
		||||
@@ -62,8 +60,6 @@ const DEVICES: DeviceRegistryEntry[] = [
 | 
			
		||||
    via_device_id: null,
 | 
			
		||||
    serial_number: null,
 | 
			
		||||
    labels: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    area_id: "backyard",
 | 
			
		||||
@@ -76,7 +72,6 @@ const DEVICES: DeviceRegistryEntry[] = [
 | 
			
		||||
    identifiers: [["demo", "pwm1"] as [string, string]],
 | 
			
		||||
    manufacturer: null,
 | 
			
		||||
    model: null,
 | 
			
		||||
    model_id: null,
 | 
			
		||||
    name_by_user: null,
 | 
			
		||||
    name: "Lamp",
 | 
			
		||||
    sw_version: null,
 | 
			
		||||
@@ -84,8 +79,6 @@ const DEVICES: DeviceRegistryEntry[] = [
 | 
			
		||||
    via_device_id: null,
 | 
			
		||||
    serial_number: null,
 | 
			
		||||
    labels: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    area_id: null,
 | 
			
		||||
@@ -98,7 +91,6 @@ const DEVICES: DeviceRegistryEntry[] = [
 | 
			
		||||
    identifiers: [["demo", "pwm1"] as [string, string]],
 | 
			
		||||
    manufacturer: null,
 | 
			
		||||
    model: null,
 | 
			
		||||
    model_id: null,
 | 
			
		||||
    name_by_user: "User name",
 | 
			
		||||
    name: "Technical name",
 | 
			
		||||
    sw_version: null,
 | 
			
		||||
@@ -106,8 +98,6 @@ const DEVICES: DeviceRegistryEntry[] = [
 | 
			
		||||
    via_device_id: null,
 | 
			
		||||
    serial_number: null,
 | 
			
		||||
    labels: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@@ -120,8 +110,6 @@ const AREAS: AreaRegistryEntry[] = [
 | 
			
		||||
    picture: null,
 | 
			
		||||
    aliases: [],
 | 
			
		||||
    labels: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    area_id: "bedroom",
 | 
			
		||||
@@ -131,8 +119,6 @@ const AREAS: AreaRegistryEntry[] = [
 | 
			
		||||
    picture: null,
 | 
			
		||||
    aliases: [],
 | 
			
		||||
    labels: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    area_id: "livingroom",
 | 
			
		||||
@@ -142,8 +128,6 @@ const AREAS: AreaRegistryEntry[] = [
 | 
			
		||||
    picture: null,
 | 
			
		||||
    aliases: [],
 | 
			
		||||
    labels: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@@ -154,8 +138,6 @@ const FLOORS: FloorRegistryEntry[] = [
 | 
			
		||||
    level: 0,
 | 
			
		||||
    icon: null,
 | 
			
		||||
    aliases: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    floor_id: "first",
 | 
			
		||||
@@ -163,8 +145,6 @@ const FLOORS: FloorRegistryEntry[] = [
 | 
			
		||||
    level: 1,
 | 
			
		||||
    icon: "mdi:numeric-1",
 | 
			
		||||
    aliases: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    floor_id: "second",
 | 
			
		||||
@@ -172,8 +152,6 @@ const FLOORS: FloorRegistryEntry[] = [
 | 
			
		||||
    level: 2,
 | 
			
		||||
    icon: "mdi:numeric-2",
 | 
			
		||||
    aliases: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@@ -184,8 +162,6 @@ const LABELS: LabelRegistryEntry[] = [
 | 
			
		||||
    icon: null,
 | 
			
		||||
    color: "yellow",
 | 
			
		||||
    description: null,
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label_id: "entertainment",
 | 
			
		||||
@@ -193,8 +169,6 @@ const LABELS: LabelRegistryEntry[] = [
 | 
			
		||||
    icon: "mdi:popcorn",
 | 
			
		||||
    color: "blue",
 | 
			
		||||
    description: null,
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -56,46 +56,48 @@ export class DemoDateTimeDateTimeNumeric extends LitElement {
 | 
			
		||||
          <div class="center">12 Hours</div>
 | 
			
		||||
          <div class="center">24 Hours</div>
 | 
			
		||||
        </div>
 | 
			
		||||
        ${Object.entries(translationMetadata.translations).map(
 | 
			
		||||
          ([key, value]) => html`
 | 
			
		||||
            <div class="container">
 | 
			
		||||
              <div>${value.nativeName}</div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatDateTimeNumeric(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.language,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
        ${Object.entries(translationMetadata.translations)
 | 
			
		||||
          .filter(([key, _]) => key !== "test")
 | 
			
		||||
          .map(
 | 
			
		||||
            ([key, value]) => html`
 | 
			
		||||
              <div class="container">
 | 
			
		||||
                <div>${value.nativeName}</div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatDateTimeNumeric(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.language,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatDateTimeNumeric(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.am_pm,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatDateTimeNumeric(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.twenty_four,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatDateTimeNumeric(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.am_pm,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatDateTimeNumeric(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.twenty_four,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          `
 | 
			
		||||
        )}
 | 
			
		||||
            `
 | 
			
		||||
          )}
 | 
			
		||||
      </mwc-list>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -56,46 +56,48 @@ export class DemoDateTimeDateTimeSeconds extends LitElement {
 | 
			
		||||
          <div class="center">12 Hours</div>
 | 
			
		||||
          <div class="center">24 Hours</div>
 | 
			
		||||
        </div>
 | 
			
		||||
        ${Object.entries(translationMetadata.translations).map(
 | 
			
		||||
          ([key, value]) => html`
 | 
			
		||||
            <div class="container">
 | 
			
		||||
              <div>${value.nativeName}</div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatDateTimeWithSeconds(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.language,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
        ${Object.entries(translationMetadata.translations)
 | 
			
		||||
          .filter(([key, _]) => key !== "test")
 | 
			
		||||
          .map(
 | 
			
		||||
            ([key, value]) => html`
 | 
			
		||||
              <div class="container">
 | 
			
		||||
                <div>${value.nativeName}</div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatDateTimeWithSeconds(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.language,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatDateTimeWithSeconds(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.am_pm,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatDateTimeWithSeconds(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.twenty_four,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatDateTimeWithSeconds(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.am_pm,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatDateTimeWithSeconds(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.twenty_four,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          `
 | 
			
		||||
        )}
 | 
			
		||||
            `
 | 
			
		||||
          )}
 | 
			
		||||
      </mwc-list>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -56,46 +56,48 @@ export class DemoDateTimeDateTimeShortYear extends LitElement {
 | 
			
		||||
          <div class="center">12 Hours</div>
 | 
			
		||||
          <div class="center">24 Hours</div>
 | 
			
		||||
        </div>
 | 
			
		||||
        ${Object.entries(translationMetadata.translations).map(
 | 
			
		||||
          ([key, value]) => html`
 | 
			
		||||
            <div class="container">
 | 
			
		||||
              <div>${value.nativeName}</div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatShortDateTimeWithYear(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.language,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
        ${Object.entries(translationMetadata.translations)
 | 
			
		||||
          .filter(([key, _]) => key !== "test")
 | 
			
		||||
          .map(
 | 
			
		||||
            ([key, value]) => html`
 | 
			
		||||
              <div class="container">
 | 
			
		||||
                <div>${value.nativeName}</div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatShortDateTimeWithYear(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.language,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatShortDateTimeWithYear(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.am_pm,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatShortDateTimeWithYear(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.twenty_four,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatShortDateTimeWithYear(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.am_pm,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatShortDateTimeWithYear(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.twenty_four,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          `
 | 
			
		||||
        )}
 | 
			
		||||
            `
 | 
			
		||||
          )}
 | 
			
		||||
      </mwc-list>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -56,46 +56,48 @@ export class DemoDateTimeDateTimeShort extends LitElement {
 | 
			
		||||
          <div class="center">12 Hours</div>
 | 
			
		||||
          <div class="center">24 Hours</div>
 | 
			
		||||
        </div>
 | 
			
		||||
        ${Object.entries(translationMetadata.translations).map(
 | 
			
		||||
          ([key, value]) => html`
 | 
			
		||||
            <div class="container">
 | 
			
		||||
              <div>${value.nativeName}</div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatShortDateTime(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.language,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
        ${Object.entries(translationMetadata.translations)
 | 
			
		||||
          .filter(([key, _]) => key !== "test")
 | 
			
		||||
          .map(
 | 
			
		||||
            ([key, value]) => html`
 | 
			
		||||
              <div class="container">
 | 
			
		||||
                <div>${value.nativeName}</div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatShortDateTime(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.language,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatShortDateTime(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.am_pm,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatShortDateTime(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.twenty_four,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatShortDateTime(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.am_pm,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatShortDateTime(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.twenty_four,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          `
 | 
			
		||||
        )}
 | 
			
		||||
            `
 | 
			
		||||
          )}
 | 
			
		||||
      </mwc-list>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -56,46 +56,48 @@ export class DemoDateTimeDateTime extends LitElement {
 | 
			
		||||
          <div class="center">12 Hours</div>
 | 
			
		||||
          <div class="center">24 Hours</div>
 | 
			
		||||
        </div>
 | 
			
		||||
        ${Object.entries(translationMetadata.translations).map(
 | 
			
		||||
          ([key, value]) => html`
 | 
			
		||||
            <div class="container">
 | 
			
		||||
              <div>${value.nativeName}</div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatDateTime(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.language,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
        ${Object.entries(translationMetadata.translations)
 | 
			
		||||
          .filter(([key, _]) => key !== "test")
 | 
			
		||||
          .map(
 | 
			
		||||
            ([key, value]) => html`
 | 
			
		||||
              <div class="container">
 | 
			
		||||
                <div>${value.nativeName}</div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatDateTime(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.language,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatDateTime(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.am_pm,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatDateTime(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.twenty_four,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatDateTime(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.am_pm,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatDateTime(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.twenty_four,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          `
 | 
			
		||||
        )}
 | 
			
		||||
            `
 | 
			
		||||
          )}
 | 
			
		||||
      </mwc-list>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -35,57 +35,59 @@ export class DemoDateTimeDate extends LitElement {
 | 
			
		||||
          <div class="center">Month-Day-Year</div>
 | 
			
		||||
          <div class="center">Year-Month-Day</div>
 | 
			
		||||
        </div>
 | 
			
		||||
        ${Object.entries(translationMetadata.translations).map(
 | 
			
		||||
          ([key, value]) => html`
 | 
			
		||||
            <div class="container">
 | 
			
		||||
              <div>${value.nativeName}</div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatDateNumeric(
 | 
			
		||||
                  date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    date_format: DateFormat.language,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
        ${Object.entries(translationMetadata.translations)
 | 
			
		||||
          .filter(([key, _]) => key !== "test")
 | 
			
		||||
          .map(
 | 
			
		||||
            ([key, value]) => html`
 | 
			
		||||
              <div class="container">
 | 
			
		||||
                <div>${value.nativeName}</div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatDateNumeric(
 | 
			
		||||
                    date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      date_format: DateFormat.language,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatDateNumeric(
 | 
			
		||||
                    date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      date_format: DateFormat.DMY,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatDateNumeric(
 | 
			
		||||
                    date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      date_format: DateFormat.MDY,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatDateNumeric(
 | 
			
		||||
                    date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      date_format: DateFormat.YMD,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatDateNumeric(
 | 
			
		||||
                  date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    date_format: DateFormat.DMY,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatDateNumeric(
 | 
			
		||||
                  date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    date_format: DateFormat.MDY,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatDateNumeric(
 | 
			
		||||
                  date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    date_format: DateFormat.YMD,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          `
 | 
			
		||||
        )}
 | 
			
		||||
            `
 | 
			
		||||
          )}
 | 
			
		||||
      </mwc-list>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -56,46 +56,48 @@ export class DemoDateTimeTimeSeconds extends LitElement {
 | 
			
		||||
          <div class="center">12 Hours</div>
 | 
			
		||||
          <div class="center">24 Hours</div>
 | 
			
		||||
        </div>
 | 
			
		||||
        ${Object.entries(translationMetadata.translations).map(
 | 
			
		||||
          ([key, value]) => html`
 | 
			
		||||
            <div class="container">
 | 
			
		||||
              <div>${value.nativeName}</div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatTimeWithSeconds(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.language,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
        ${Object.entries(translationMetadata.translations)
 | 
			
		||||
          .filter(([key, _]) => key !== "test")
 | 
			
		||||
          .map(
 | 
			
		||||
            ([key, value]) => html`
 | 
			
		||||
              <div class="container">
 | 
			
		||||
                <div>${value.nativeName}</div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatTimeWithSeconds(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.language,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatTimeWithSeconds(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.am_pm,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatTimeWithSeconds(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.twenty_four,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatTimeWithSeconds(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.am_pm,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatTimeWithSeconds(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.twenty_four,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          `
 | 
			
		||||
        )}
 | 
			
		||||
            `
 | 
			
		||||
          )}
 | 
			
		||||
      </mwc-list>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -56,46 +56,48 @@ export class DemoDateTimeTimeWeekday extends LitElement {
 | 
			
		||||
          <div class="center">12 Hours</div>
 | 
			
		||||
          <div class="center">24 Hours</div>
 | 
			
		||||
        </div>
 | 
			
		||||
        ${Object.entries(translationMetadata.translations).map(
 | 
			
		||||
          ([key, value]) => html`
 | 
			
		||||
            <div class="container">
 | 
			
		||||
              <div>${value.nativeName}</div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatTimeWeekday(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.language,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
        ${Object.entries(translationMetadata.translations)
 | 
			
		||||
          .filter(([key, _]) => key !== "test")
 | 
			
		||||
          .map(
 | 
			
		||||
            ([key, value]) => html`
 | 
			
		||||
              <div class="container">
 | 
			
		||||
                <div>${value.nativeName}</div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatTimeWeekday(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.language,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatTimeWeekday(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.am_pm,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatTimeWeekday(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.twenty_four,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatTimeWeekday(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.am_pm,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatTimeWeekday(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.twenty_four,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          `
 | 
			
		||||
        )}
 | 
			
		||||
            `
 | 
			
		||||
          )}
 | 
			
		||||
      </mwc-list>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -56,46 +56,48 @@ export class DemoDateTimeTime extends LitElement {
 | 
			
		||||
          <div class="center">12 Hours</div>
 | 
			
		||||
          <div class="center">24 Hours</div>
 | 
			
		||||
        </div>
 | 
			
		||||
        ${Object.entries(translationMetadata.translations).map(
 | 
			
		||||
          ([key, value]) => html`
 | 
			
		||||
            <div class="container">
 | 
			
		||||
              <div>${value.nativeName}</div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatTime(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.language,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
        ${Object.entries(translationMetadata.translations)
 | 
			
		||||
          .filter(([key, _]) => key !== "test")
 | 
			
		||||
          .map(
 | 
			
		||||
            ([key, value]) => html`
 | 
			
		||||
              <div class="container">
 | 
			
		||||
                <div>${value.nativeName}</div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatTime(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.language,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatTime(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.am_pm,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="center">
 | 
			
		||||
                  ${formatTime(
 | 
			
		||||
                    this.date,
 | 
			
		||||
                    {
 | 
			
		||||
                      ...defaultLocale,
 | 
			
		||||
                      language: key,
 | 
			
		||||
                      time_format: TimeFormat.twenty_four,
 | 
			
		||||
                    },
 | 
			
		||||
                    demoConfig
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatTime(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.am_pm,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="center">
 | 
			
		||||
                ${formatTime(
 | 
			
		||||
                  this.date,
 | 
			
		||||
                  {
 | 
			
		||||
                    ...defaultLocale,
 | 
			
		||||
                    language: key,
 | 
			
		||||
                    time_format: TimeFormat.twenty_four,
 | 
			
		||||
                  },
 | 
			
		||||
                  demoConfig
 | 
			
		||||
                )}
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          `
 | 
			
		||||
        )}
 | 
			
		||||
            `
 | 
			
		||||
          )}
 | 
			
		||||
      </mwc-list>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -287,11 +287,11 @@ const CONFIGS = [
 | 
			
		||||
    config: `
 | 
			
		||||
- type: entities
 | 
			
		||||
  entities:
 | 
			
		||||
    - type: perform-action
 | 
			
		||||
    - type: call-service
 | 
			
		||||
      icon: mdi:power
 | 
			
		||||
      name: Bed light
 | 
			
		||||
      action_name: Toggle light
 | 
			
		||||
      action: light.toggle
 | 
			
		||||
      service: light.toggle
 | 
			
		||||
      data:
 | 
			
		||||
        entity_id: light.bed_light
 | 
			
		||||
    - type: section
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +0,0 @@
 | 
			
		||||
---
 | 
			
		||||
title: Picture Card
 | 
			
		||||
---
 | 
			
		||||
@@ -1,61 +0,0 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import { getEntity } from "../../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../../components/demo-cards";
 | 
			
		||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
 | 
			
		||||
 | 
			
		||||
const ENTITIES = [
 | 
			
		||||
  getEntity("person", "paulus", "home", {
 | 
			
		||||
    friendly_name: "Paulus",
 | 
			
		||||
    entity_picture: "/images/paulus.jpg",
 | 
			
		||||
  }),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const CONFIGS = [
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Image URL",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: picture
 | 
			
		||||
  image: /images/living_room.png
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Person entity",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: picture
 | 
			
		||||
  image_entity: person.paulus
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Error: Image required",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: picture
 | 
			
		||||
  entity: person.paulus
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@customElement("demo-lovelace-picture-card")
 | 
			
		||||
class DemoPicture extends LitElement {
 | 
			
		||||
  @query("#demos") private _demoRoot!: HTMLElement;
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
    return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected firstUpdated(changedProperties: PropertyValues) {
 | 
			
		||||
    super.firstUpdated(changedProperties);
 | 
			
		||||
    const hass = provideHass(this._demoRoot);
 | 
			
		||||
    hass.updateTranslations(null, "en");
 | 
			
		||||
    hass.updateTranslations("lovelace", "en");
 | 
			
		||||
    hass.addEntities(ENTITIES);
 | 
			
		||||
    mockIcons(hass);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-lovelace-picture-card": DemoPicture;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -25,15 +25,6 @@ const ENTITIES = [
 | 
			
		||||
    friendly_name: "Movement Backyard",
 | 
			
		||||
    device_class: "motion",
 | 
			
		||||
  }),
 | 
			
		||||
  getEntity("person", "paulus", "home", {
 | 
			
		||||
    friendly_name: "Paulus",
 | 
			
		||||
    entity_picture: "/images/paulus.jpg",
 | 
			
		||||
  }),
 | 
			
		||||
  getEntity("sensor", "battery", 35, {
 | 
			
		||||
    device_class: "battery",
 | 
			
		||||
    friendly_name: "Battery",
 | 
			
		||||
    unit_of_measurement: "%",
 | 
			
		||||
  }),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const CONFIGS = [
 | 
			
		||||
@@ -132,19 +123,6 @@ const CONFIGS = [
 | 
			
		||||
        left: 35%
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Person entity",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: picture-elements
 | 
			
		||||
  image_entity: person.paulus
 | 
			
		||||
  elements:
 | 
			
		||||
  - type: state-icon
 | 
			
		||||
    entity: sensor.battery
 | 
			
		||||
    style:
 | 
			
		||||
      top: 8%
 | 
			
		||||
      left: 8%
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@customElement("demo-lovelace-picture-elements-card")
 | 
			
		||||
 
 | 
			
		||||
@@ -12,10 +12,6 @@ const ENTITIES = [
 | 
			
		||||
  getEntity("light", "bed_light", "off", {
 | 
			
		||||
    friendly_name: "Bed Light",
 | 
			
		||||
  }),
 | 
			
		||||
  getEntity("person", "paulus", "home", {
 | 
			
		||||
    friendly_name: "Paulus",
 | 
			
		||||
    entity_picture: "/images/paulus.jpg",
 | 
			
		||||
  }),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const CONFIGS = [
 | 
			
		||||
@@ -54,13 +50,6 @@ const CONFIGS = [
 | 
			
		||||
  entity: camera.demo_camera
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Person entity",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: picture-entity
 | 
			
		||||
  entity: person.paulus
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Hidden name",
 | 
			
		||||
    config: `
 | 
			
		||||
 
 | 
			
		||||
@@ -20,15 +20,6 @@ const ENTITIES = [
 | 
			
		||||
    friendly_name: "Basement Floor Wet",
 | 
			
		||||
    device_class: "moisture",
 | 
			
		||||
  }),
 | 
			
		||||
  getEntity("person", "paulus", "home", {
 | 
			
		||||
    friendly_name: "Paulus",
 | 
			
		||||
    entity_picture: "/images/paulus.jpg",
 | 
			
		||||
  }),
 | 
			
		||||
  getEntity("sensor", "battery", 35, {
 | 
			
		||||
    device_class: "battery",
 | 
			
		||||
    friendly_name: "Battery",
 | 
			
		||||
    unit_of_measurement: "%",
 | 
			
		||||
  }),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const CONFIGS = [
 | 
			
		||||
@@ -99,15 +90,6 @@ const CONFIGS = [
 | 
			
		||||
    - light.ceiling_lights
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Person entity",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: picture-glance
 | 
			
		||||
  image_entity: person.paulus
 | 
			
		||||
  entities:
 | 
			
		||||
    - sensor.battery
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Custom icon",
 | 
			
		||||
    config: `
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@ import { getEntity } from "../../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../../components/demo-cards";
 | 
			
		||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
 | 
			
		||||
import { ClimateEntityFeature } from "../../../../src/data/climate";
 | 
			
		||||
 | 
			
		||||
const ENTITIES = [
 | 
			
		||||
  getEntity("switch", "tv_outlet", "on", {
 | 
			
		||||
@@ -61,36 +60,6 @@ const ENTITIES = [
 | 
			
		||||
      CoverEntityFeature.OPEN_TILT +
 | 
			
		||||
      CoverEntityFeature.STOP_TILT,
 | 
			
		||||
  }),
 | 
			
		||||
  getEntity("input_number", "counter", "1.0", {
 | 
			
		||||
    friendly_name: "Counter",
 | 
			
		||||
    initial: 0,
 | 
			
		||||
    min: 0,
 | 
			
		||||
    max: 100,
 | 
			
		||||
    step: 1,
 | 
			
		||||
    mode: "slider",
 | 
			
		||||
  }),
 | 
			
		||||
  getEntity("climate", "dual_thermostat", "heat/cool", {
 | 
			
		||||
    friendly_name: "Dual thermostat",
 | 
			
		||||
    hvac_modes: ["off", "cool", "heat_cool", "auto", "dry", "fan_only"],
 | 
			
		||||
    min_temp: 7,
 | 
			
		||||
    max_temp: 35,
 | 
			
		||||
    fan_modes: ["on_low", "on_high", "auto_low", "auto_high", "off"],
 | 
			
		||||
    preset_modes: ["home", "eco", "away"],
 | 
			
		||||
    swing_modes: ["auto", "1", "2", "3", "off"],
 | 
			
		||||
    current_temperature: 23,
 | 
			
		||||
    target_temp_high: 24,
 | 
			
		||||
    target_temp_low: 21,
 | 
			
		||||
    fan_mode: "auto_low",
 | 
			
		||||
    preset_mode: "home",
 | 
			
		||||
    swing_mode: "auto",
 | 
			
		||||
    supported_features:
 | 
			
		||||
      ClimateEntityFeature.TURN_ON +
 | 
			
		||||
      ClimateEntityFeature.TURN_OFF +
 | 
			
		||||
      ClimateEntityFeature.SWING_MODE +
 | 
			
		||||
      ClimateEntityFeature.PRESET_MODE +
 | 
			
		||||
      ClimateEntityFeature.FAN_MODE +
 | 
			
		||||
      ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
 | 
			
		||||
  }),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const CONFIGS = [
 | 
			
		||||
@@ -224,25 +193,6 @@ const CONFIGS = [
 | 
			
		||||
  - type: "cover-tilt"
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Number buttons feature",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: tile
 | 
			
		||||
  entity: input_number.counter
 | 
			
		||||
  features:
 | 
			
		||||
  - type: numeric-input
 | 
			
		||||
    style: buttons
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Dual thermostat feature",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: tile
 | 
			
		||||
  entity: climate.dual_thermostat
 | 
			
		||||
  features:
 | 
			
		||||
  - type: target-temperature
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@customElement("demo-lovelace-tile-card")
 | 
			
		||||
 
 | 
			
		||||
@@ -140,9 +140,6 @@ const ENTITIES: HassEntity[] = [
 | 
			
		||||
  createEntity("climate.auto_preheating", "auto", undefined, {
 | 
			
		||||
    hvac_action: "preheating",
 | 
			
		||||
  }),
 | 
			
		||||
  createEntity("climate.auto_defrosting", "auto", undefined, {
 | 
			
		||||
    hvac_action: "defrosting",
 | 
			
		||||
  }),
 | 
			
		||||
  createEntity("climate.auto_heating", "auto", undefined, {
 | 
			
		||||
    hvac_action: "heating",
 | 
			
		||||
  }),
 | 
			
		||||
@@ -358,18 +355,19 @@ export class DemoEntityState extends LitElement {
 | 
			
		||||
        },
 | 
			
		||||
        entity_id: {
 | 
			
		||||
          title: "Entity ID",
 | 
			
		||||
          width: "30%",
 | 
			
		||||
          filterable: true,
 | 
			
		||||
          sortable: true,
 | 
			
		||||
        },
 | 
			
		||||
        state: {
 | 
			
		||||
          title: "State",
 | 
			
		||||
          width: "20%",
 | 
			
		||||
          sortable: true,
 | 
			
		||||
          template: (entry) =>
 | 
			
		||||
            html`${computeStateDisplay(
 | 
			
		||||
              hass.localize,
 | 
			
		||||
              entry.stateObj,
 | 
			
		||||
              hass.locale,
 | 
			
		||||
              [], // numericDeviceClasses
 | 
			
		||||
              hass.config,
 | 
			
		||||
              hass.entities
 | 
			
		||||
            )}`,
 | 
			
		||||
@@ -377,12 +375,14 @@ export class DemoEntityState extends LitElement {
 | 
			
		||||
        device_class: {
 | 
			
		||||
          title: "Device class",
 | 
			
		||||
          template: (entry) => html`${entry.device_class ?? "-"}`,
 | 
			
		||||
          width: "20%",
 | 
			
		||||
          filterable: true,
 | 
			
		||||
          sortable: true,
 | 
			
		||||
        },
 | 
			
		||||
        domain: {
 | 
			
		||||
          title: "Domain",
 | 
			
		||||
          template: (entry) => html`${computeDomain(entry.entity_id)}`,
 | 
			
		||||
          width: "20%",
 | 
			
		||||
          filterable: true,
 | 
			
		||||
          sortable: true,
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -203,8 +203,6 @@ const createEntityRegistryEntries = (
 | 
			
		||||
    options: null,
 | 
			
		||||
    labels: [],
 | 
			
		||||
    categories: {},
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@@ -217,7 +215,6 @@ const createDeviceRegistryEntries = (
 | 
			
		||||
    connections: [],
 | 
			
		||||
    manufacturer: "ESPHome",
 | 
			
		||||
    model: "Mock Device",
 | 
			
		||||
    model_id: "ABC-001",
 | 
			
		||||
    name: "Tag Reader",
 | 
			
		||||
    sw_version: null,
 | 
			
		||||
    hw_version: "1.0.0",
 | 
			
		||||
@@ -230,8 +227,6 @@ const createDeviceRegistryEntries = (
 | 
			
		||||
    disabled_by: null,
 | 
			
		||||
    configuration_url: null,
 | 
			
		||||
    labels: [],
 | 
			
		||||
    created_at: 0,
 | 
			
		||||
    modified_at: 0,
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -127,13 +127,14 @@ export class HassioBackups extends LitElement {
 | 
			
		||||
        main: true,
 | 
			
		||||
        sortable: true,
 | 
			
		||||
        filterable: true,
 | 
			
		||||
        flex: 2,
 | 
			
		||||
        grows: true,
 | 
			
		||||
        template: (backup) =>
 | 
			
		||||
          html`${backup.name || backup.slug}
 | 
			
		||||
            <div class="secondary">${backup.secondary}</div>`,
 | 
			
		||||
      },
 | 
			
		||||
      size: {
 | 
			
		||||
        title: this.supervisor.localize("backup.size"),
 | 
			
		||||
        width: "15%",
 | 
			
		||||
        hidden: narrow,
 | 
			
		||||
        filterable: true,
 | 
			
		||||
        sortable: true,
 | 
			
		||||
@@ -141,6 +142,7 @@ export class HassioBackups extends LitElement {
 | 
			
		||||
      },
 | 
			
		||||
      location: {
 | 
			
		||||
        title: this.supervisor.localize("backup.location"),
 | 
			
		||||
        width: "15%",
 | 
			
		||||
        hidden: narrow,
 | 
			
		||||
        filterable: true,
 | 
			
		||||
        sortable: true,
 | 
			
		||||
@@ -149,6 +151,7 @@ export class HassioBackups extends LitElement {
 | 
			
		||||
      },
 | 
			
		||||
      date: {
 | 
			
		||||
        title: this.supervisor.localize("backup.created"),
 | 
			
		||||
        width: "15%",
 | 
			
		||||
        direction: "desc",
 | 
			
		||||
        hidden: narrow,
 | 
			
		||||
        filterable: true,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,6 @@
 | 
			
		||||
import type { IFuseOptions } from "fuse.js";
 | 
			
		||||
import Fuse from "fuse.js";
 | 
			
		||||
import { stripDiacritics } from "../../../src/common/string/strip-diacritics";
 | 
			
		||||
import type { IFuseOptions } from "fuse.js";
 | 
			
		||||
import { StoreAddon } from "../../../src/data/supervisor/store";
 | 
			
		||||
import { getStripDiacriticsFn } from "../../../src/util/fuse";
 | 
			
		||||
 | 
			
		||||
export function filterAndSort(addons: StoreAddon[], filter: string) {
 | 
			
		||||
  const options: IFuseOptions<StoreAddon> = {
 | 
			
		||||
@@ -10,8 +8,7 @@ export function filterAndSort(addons: StoreAddon[], filter: string) {
 | 
			
		||||
    isCaseSensitive: false,
 | 
			
		||||
    minMatchCharLength: Math.min(filter.length, 2),
 | 
			
		||||
    threshold: 0.2,
 | 
			
		||||
    getFn: getStripDiacriticsFn,
 | 
			
		||||
  };
 | 
			
		||||
  const fuse = new Fuse(addons, options);
 | 
			
		||||
  return fuse.search(stripDiacritics(filter)).map((result) => result.item);
 | 
			
		||||
  return fuse.search(filter).map((result) => result.item);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,19 @@
 | 
			
		||||
import { mdiRefresh, mdiStorePlus } from "@mdi/js";
 | 
			
		||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
 | 
			
		||||
import { mdiStorePlus, mdiUpdate } from "@mdi/js";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import { atLeastVersion } from "../../../src/common/config/version";
 | 
			
		||||
import { fireEvent } from "../../../src/common/dom/fire_event";
 | 
			
		||||
import "../../../src/components/ha-fab";
 | 
			
		||||
import { reloadHassioAddons } from "../../../src/data/hassio/addon";
 | 
			
		||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
 | 
			
		||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
 | 
			
		||||
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
 | 
			
		||||
import "../../../src/layouts/hass-subpage";
 | 
			
		||||
import "../../../src/layouts/hass-tabs-subpage";
 | 
			
		||||
import { haStyle } from "../../../src/resources/styles";
 | 
			
		||||
import { HomeAssistant, Route } from "../../../src/types";
 | 
			
		||||
import { supervisorTabs } from "../hassio-tabs";
 | 
			
		||||
import "./hassio-addons";
 | 
			
		||||
import "../../../src/layouts/hass-subpage";
 | 
			
		||||
import { reloadHassioAddons } from "../../../src/data/hassio/addon";
 | 
			
		||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
 | 
			
		||||
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
 | 
			
		||||
import { fireEvent } from "../../../src/common/dom/fire_event";
 | 
			
		||||
 | 
			
		||||
@customElement("hassio-dashboard")
 | 
			
		||||
class HassioDashboard extends LitElement {
 | 
			
		||||
@@ -43,7 +43,7 @@ class HassioDashboard extends LitElement {
 | 
			
		||||
        <ha-icon-button
 | 
			
		||||
          slot="toolbar-icon"
 | 
			
		||||
          @click=${this._handleCheckUpdates}
 | 
			
		||||
          .path=${mdiRefresh}
 | 
			
		||||
          .path=${mdiUpdate}
 | 
			
		||||
          .label=${this.supervisor.localize("store.check_updates")}
 | 
			
		||||
        ></ha-icon-button>
 | 
			
		||||
        <hassio-addons
 | 
			
		||||
 
 | 
			
		||||
@@ -66,8 +66,7 @@ class HassioRepositoriesDialog extends LitElement {
 | 
			
		||||
          repo.slug !== "core" && // The core add-ons repository
 | 
			
		||||
          repo.slug !== "local" && // Locally managed add-ons
 | 
			
		||||
          repo.slug !== "a0d7b954" && // Home Assistant Community Add-ons
 | 
			
		||||
          repo.slug !== "5c53de3b" && // The ESPHome repository
 | 
			
		||||
          repo.slug !== "d5369777" // Music Assistant repository
 | 
			
		||||
          repo.slug !== "5c53de3b" // The ESPHome repository
 | 
			
		||||
      )
 | 
			
		||||
      .sort((a, b) =>
 | 
			
		||||
        caseInsensitiveStringCompare(a.name, b.name, this.hass.locale.language)
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,11 @@
 | 
			
		||||
    el.src = src;
 | 
			
		||||
    document.body.appendChild(el);
 | 
			
		||||
  }
 | 
			
		||||
  if (<%= modernRegex %>.test(navigator.userAgent)) {
 | 
			
		||||
  if (/.*Version\/(?:11|12)(?:\.\d+)*.*Safari\//.test(navigator.userAgent)) {
 | 
			
		||||
    <% for (const entry of es5EntryJS) { %>
 | 
			
		||||
      loadES5("<%= entry %>");
 | 
			
		||||
    <% } %>
 | 
			
		||||
  } else {
 | 
			
		||||
    try {
 | 
			
		||||
        <% for (const entry of latestEntryJS) { %>
 | 
			
		||||
          new Function("import('<%= entry %>')")();
 | 
			
		||||
@@ -13,10 +17,6 @@
 | 
			
		||||
      <% for (const entry of es5EntryJS) { %>
 | 
			
		||||
        loadES5("<%= entry %>");
 | 
			
		||||
      <% } %>
 | 
			
		||||
  } else {
 | 
			
		||||
    <% for (const entry of es5EntryJS) { %>
 | 
			
		||||
      loadES5("<%= entry %>");
 | 
			
		||||
    <% } %>
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
// Compat needs to be first import
 | 
			
		||||
import "../../src/resources/compatibility";
 | 
			
		||||
import "../../src/resources/safari-14-attachshadow-patch";
 | 
			
		||||
import "./hassio-main";
 | 
			
		||||
 | 
			
		||||
import("../../src/resources/ha-style");
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										163
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										163
									
								
								package.json
									
									
									
									
									
								
							@@ -16,7 +16,7 @@
 | 
			
		||||
    "lint:lit": "lit-analyzer \"{.,*}/src/**/*.ts\"",
 | 
			
		||||
    "lint": "yarn run lint:eslint && yarn run lint:prettier && yarn run lint:types && yarn run lint:lit",
 | 
			
		||||
    "format": "yarn run format:eslint && yarn run format:prettier",
 | 
			
		||||
    "postinstall": "husky",
 | 
			
		||||
    "postinstall": "husky install",
 | 
			
		||||
    "prepack": "pinst --disable",
 | 
			
		||||
    "postpack": "pinst --enable",
 | 
			
		||||
    "test": "instant-mocha --webpack-config ./test/webpack.config.js --require ./test/setup.cjs \"test/**/*.ts\""
 | 
			
		||||
@@ -25,35 +25,35 @@
 | 
			
		||||
  "license": "Apache-2.0",
 | 
			
		||||
  "type": "module",
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@babel/runtime": "7.25.0",
 | 
			
		||||
    "@braintree/sanitize-url": "7.1.0",
 | 
			
		||||
    "@codemirror/autocomplete": "6.18.0",
 | 
			
		||||
    "@codemirror/commands": "6.6.0",
 | 
			
		||||
    "@codemirror/language": "6.10.2",
 | 
			
		||||
    "@babel/runtime": "7.24.4",
 | 
			
		||||
    "@braintree/sanitize-url": "7.0.1",
 | 
			
		||||
    "@codemirror/autocomplete": "6.16.0",
 | 
			
		||||
    "@codemirror/commands": "6.5.0",
 | 
			
		||||
    "@codemirror/language": "6.10.1",
 | 
			
		||||
    "@codemirror/legacy-modes": "6.4.0",
 | 
			
		||||
    "@codemirror/search": "6.5.6",
 | 
			
		||||
    "@codemirror/state": "6.4.1",
 | 
			
		||||
    "@codemirror/view": "6.30.0",
 | 
			
		||||
    "@codemirror/view": "6.26.3",
 | 
			
		||||
    "@egjs/hammerjs": "2.0.17",
 | 
			
		||||
    "@formatjs/intl-datetimeformat": "6.12.5",
 | 
			
		||||
    "@formatjs/intl-displaynames": "6.6.8",
 | 
			
		||||
    "@formatjs/intl-datetimeformat": "6.12.3",
 | 
			
		||||
    "@formatjs/intl-displaynames": "6.6.6",
 | 
			
		||||
    "@formatjs/intl-getcanonicallocales": "2.3.0",
 | 
			
		||||
    "@formatjs/intl-listformat": "7.5.7",
 | 
			
		||||
    "@formatjs/intl-locale": "4.0.0",
 | 
			
		||||
    "@formatjs/intl-numberformat": "8.10.3",
 | 
			
		||||
    "@formatjs/intl-pluralrules": "5.2.14",
 | 
			
		||||
    "@formatjs/intl-relativetimeformat": "11.2.14",
 | 
			
		||||
    "@fullcalendar/core": "6.1.15",
 | 
			
		||||
    "@fullcalendar/daygrid": "6.1.15",
 | 
			
		||||
    "@fullcalendar/interaction": "6.1.15",
 | 
			
		||||
    "@fullcalendar/list": "6.1.15",
 | 
			
		||||
    "@fullcalendar/luxon3": "6.1.15",
 | 
			
		||||
    "@fullcalendar/timegrid": "6.1.15",
 | 
			
		||||
    "@formatjs/intl-listformat": "7.5.5",
 | 
			
		||||
    "@formatjs/intl-locale": "3.4.5",
 | 
			
		||||
    "@formatjs/intl-numberformat": "8.10.1",
 | 
			
		||||
    "@formatjs/intl-pluralrules": "5.2.12",
 | 
			
		||||
    "@formatjs/intl-relativetimeformat": "11.2.12",
 | 
			
		||||
    "@fullcalendar/core": "6.1.11",
 | 
			
		||||
    "@fullcalendar/daygrid": "6.1.11",
 | 
			
		||||
    "@fullcalendar/interaction": "6.1.11",
 | 
			
		||||
    "@fullcalendar/list": "6.1.11",
 | 
			
		||||
    "@fullcalendar/luxon3": "6.1.11",
 | 
			
		||||
    "@fullcalendar/timegrid": "6.1.11",
 | 
			
		||||
    "@lezer/highlight": "1.2.0",
 | 
			
		||||
    "@lit-labs/context": "0.4.1",
 | 
			
		||||
    "@lit-labs/motion": "1.0.7",
 | 
			
		||||
    "@lit-labs/observers": "2.0.2",
 | 
			
		||||
    "@lit-labs/virtualizer": "2.0.14",
 | 
			
		||||
    "@lit-labs/virtualizer": "2.0.12",
 | 
			
		||||
    "@lrnwebcomponents/simple-tooltip": "8.0.2",
 | 
			
		||||
    "@material/chips": "=14.0.0-canary.53b3cad2f.0",
 | 
			
		||||
    "@material/data-table": "=14.0.0-canary.53b3cad2f.0",
 | 
			
		||||
@@ -70,6 +70,7 @@
 | 
			
		||||
    "@material/mwc-list": "0.27.0",
 | 
			
		||||
    "@material/mwc-menu": "0.27.0",
 | 
			
		||||
    "@material/mwc-radio": "0.27.0",
 | 
			
		||||
    "@material/mwc-ripple": "0.27.0",
 | 
			
		||||
    "@material/mwc-select": "0.27.0",
 | 
			
		||||
    "@material/mwc-snackbar": "0.27.0",
 | 
			
		||||
    "@material/mwc-switch": "0.27.0",
 | 
			
		||||
@@ -80,7 +81,7 @@
 | 
			
		||||
    "@material/mwc-top-app-bar": "0.27.0",
 | 
			
		||||
    "@material/mwc-top-app-bar-fixed": "0.27.0",
 | 
			
		||||
    "@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
 | 
			
		||||
    "@material/web": "2.0.0",
 | 
			
		||||
    "@material/web": "1.4.1",
 | 
			
		||||
    "@mdi/js": "7.4.47",
 | 
			
		||||
    "@mdi/svg": "7.4.47",
 | 
			
		||||
    "@polymer/paper-item": "3.0.1",
 | 
			
		||||
@@ -88,8 +89,8 @@
 | 
			
		||||
    "@polymer/paper-tabs": "3.1.0",
 | 
			
		||||
    "@polymer/polymer": "3.5.1",
 | 
			
		||||
    "@thomasloven/round-slider": "0.6.0",
 | 
			
		||||
    "@vaadin/combo-box": "24.4.5",
 | 
			
		||||
    "@vaadin/vaadin-themable-mixin": "24.4.5",
 | 
			
		||||
    "@vaadin/combo-box": "24.3.11",
 | 
			
		||||
    "@vaadin/vaadin-themable-mixin": "24.3.11",
 | 
			
		||||
    "@vibrant/color": "3.2.1-alpha.1",
 | 
			
		||||
    "@vibrant/core": "3.2.1-alpha.1",
 | 
			
		||||
    "@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
 | 
			
		||||
@@ -97,10 +98,10 @@
 | 
			
		||||
    "@webcomponents/scoped-custom-element-registry": "0.0.9",
 | 
			
		||||
    "@webcomponents/webcomponentsjs": "2.8.0",
 | 
			
		||||
    "app-datepicker": "5.1.1",
 | 
			
		||||
    "chart.js": "4.4.3",
 | 
			
		||||
    "chart.js": "4.4.2",
 | 
			
		||||
    "color-name": "2.0.0",
 | 
			
		||||
    "comlink": "4.4.1",
 | 
			
		||||
    "core-js": "3.38.0",
 | 
			
		||||
    "core-js": "3.37.0",
 | 
			
		||||
    "cropperjs": "1.6.2",
 | 
			
		||||
    "date-fns": "3.6.0",
 | 
			
		||||
    "date-fns-tz": "3.1.3",
 | 
			
		||||
@@ -110,142 +111,141 @@
 | 
			
		||||
    "fuse.js": "7.0.0",
 | 
			
		||||
    "google-timezones-json": "1.2.0",
 | 
			
		||||
    "hls.js": "patch:hls.js@npm%3A1.5.7#~/.yarn/patches/hls.js-npm-1.5.7-f5bbd3d060.patch",
 | 
			
		||||
    "home-assistant-js-websocket": "9.4.0",
 | 
			
		||||
    "home-assistant-js-websocket": "9.3.0",
 | 
			
		||||
    "idb-keyval": "6.2.1",
 | 
			
		||||
    "intl-messageformat": "10.5.14",
 | 
			
		||||
    "intl-messageformat": "10.5.11",
 | 
			
		||||
    "js-yaml": "4.1.0",
 | 
			
		||||
    "leaflet": "1.9.4",
 | 
			
		||||
    "leaflet-draw": "1.0.4",
 | 
			
		||||
    "lit": "2.8.0",
 | 
			
		||||
    "luxon": "3.5.0",
 | 
			
		||||
    "marked": "13.0.3",
 | 
			
		||||
    "luxon": "3.4.4",
 | 
			
		||||
    "marked": "12.0.2",
 | 
			
		||||
    "memoize-one": "6.0.0",
 | 
			
		||||
    "node-vibrant": "3.2.1-alpha.1",
 | 
			
		||||
    "proxy-polyfill": "0.3.2",
 | 
			
		||||
    "punycode": "2.3.1",
 | 
			
		||||
    "qr-scanner": "1.4.2",
 | 
			
		||||
    "qrcode": "1.5.4",
 | 
			
		||||
    "qrcode": "1.5.3",
 | 
			
		||||
    "roboto-fontface": "0.10.0",
 | 
			
		||||
    "rrule": "2.8.1",
 | 
			
		||||
    "sortablejs": "1.15.2",
 | 
			
		||||
    "stacktrace-js": "2.0.2",
 | 
			
		||||
    "superstruct": "2.0.2",
 | 
			
		||||
    "superstruct": "1.0.4",
 | 
			
		||||
    "tinykeys": "2.1.0",
 | 
			
		||||
    "tsparticles-engine": "2.12.0",
 | 
			
		||||
    "tsparticles-preset-links": "2.12.0",
 | 
			
		||||
    "ua-parser-js": "1.0.38",
 | 
			
		||||
    "ua-parser-js": "1.0.37",
 | 
			
		||||
    "unfetch": "5.0.0",
 | 
			
		||||
    "vis-data": "7.1.9",
 | 
			
		||||
    "vis-network": "9.1.9",
 | 
			
		||||
    "vue": "2.7.16",
 | 
			
		||||
    "vue2-daterange-picker": "0.6.8",
 | 
			
		||||
    "weekstart": "2.0.0",
 | 
			
		||||
    "workbox-cacheable-response": "7.1.0",
 | 
			
		||||
    "workbox-core": "7.1.0",
 | 
			
		||||
    "workbox-expiration": "7.1.0",
 | 
			
		||||
    "workbox-precaching": "7.1.0",
 | 
			
		||||
    "workbox-routing": "7.1.0",
 | 
			
		||||
    "workbox-strategies": "7.1.0",
 | 
			
		||||
    "workbox-cacheable-response": "7.0.0",
 | 
			
		||||
    "workbox-core": "7.0.0",
 | 
			
		||||
    "workbox-expiration": "7.0.0",
 | 
			
		||||
    "workbox-precaching": "7.0.0",
 | 
			
		||||
    "workbox-routing": "7.0.0",
 | 
			
		||||
    "workbox-strategies": "7.0.0",
 | 
			
		||||
    "xss": "1.0.15"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@babel/core": "7.25.2",
 | 
			
		||||
    "@babel/helper-define-polyfill-provider": "0.6.2",
 | 
			
		||||
    "@babel/plugin-proposal-decorators": "7.24.7",
 | 
			
		||||
    "@babel/plugin-transform-runtime": "7.24.7",
 | 
			
		||||
    "@babel/preset-env": "7.25.3",
 | 
			
		||||
    "@babel/preset-typescript": "7.24.7",
 | 
			
		||||
    "@bundle-stats/plugin-webpack-filter": "4.14.0",
 | 
			
		||||
    "@babel/core": "7.24.4",
 | 
			
		||||
    "@babel/helper-define-polyfill-provider": "0.6.1",
 | 
			
		||||
    "@babel/plugin-proposal-decorators": "7.24.1",
 | 
			
		||||
    "@babel/plugin-transform-runtime": "7.24.3",
 | 
			
		||||
    "@babel/preset-env": "7.24.4",
 | 
			
		||||
    "@babel/preset-typescript": "7.24.1",
 | 
			
		||||
    "@bundle-stats/plugin-webpack-filter": "4.12.2",
 | 
			
		||||
    "@koa/cors": "5.0.0",
 | 
			
		||||
    "@lokalise/node-api": "12.7.0",
 | 
			
		||||
    "@lokalise/node-api": "12.4.0",
 | 
			
		||||
    "@octokit/auth-oauth-device": "7.1.1",
 | 
			
		||||
    "@octokit/plugin-retry": "7.1.1",
 | 
			
		||||
    "@octokit/rest": "21.0.1",
 | 
			
		||||
    "@octokit/plugin-retry": "7.1.0",
 | 
			
		||||
    "@octokit/rest": "20.1.0",
 | 
			
		||||
    "@open-wc/dev-server-hmr": "0.1.4",
 | 
			
		||||
    "@rollup/plugin-babel": "6.0.4",
 | 
			
		||||
    "@rollup/plugin-commonjs": "26.0.1",
 | 
			
		||||
    "@rollup/plugin-commonjs": "25.0.7",
 | 
			
		||||
    "@rollup/plugin-json": "6.1.0",
 | 
			
		||||
    "@rollup/plugin-node-resolve": "15.2.3",
 | 
			
		||||
    "@rollup/plugin-replace": "5.0.7",
 | 
			
		||||
    "@rollup/plugin-replace": "5.0.5",
 | 
			
		||||
    "@types/babel__plugin-transform-runtime": "7.9.5",
 | 
			
		||||
    "@types/chromecast-caf-receiver": "6.0.16",
 | 
			
		||||
    "@types/chromecast-caf-sender": "1.0.10",
 | 
			
		||||
    "@types/chromecast-caf-receiver": "6.0.14",
 | 
			
		||||
    "@types/chromecast-caf-sender": "1.0.9",
 | 
			
		||||
    "@types/color-name": "1.1.4",
 | 
			
		||||
    "@types/glob": "8.1.0",
 | 
			
		||||
    "@types/html-minifier-terser": "7.0.2",
 | 
			
		||||
    "@types/js-yaml": "4.0.9",
 | 
			
		||||
    "@types/leaflet": "1.9.12",
 | 
			
		||||
    "@types/leaflet": "1.9.11",
 | 
			
		||||
    "@types/leaflet-draw": "1.0.11",
 | 
			
		||||
    "@types/lodash.merge": "4.6.9",
 | 
			
		||||
    "@types/luxon": "3.4.2",
 | 
			
		||||
    "@types/mocha": "10.0.7",
 | 
			
		||||
    "@types/mocha": "10.0.6",
 | 
			
		||||
    "@types/qrcode": "1.5.5",
 | 
			
		||||
    "@types/serve-handler": "6.1.4",
 | 
			
		||||
    "@types/sortablejs": "1.15.8",
 | 
			
		||||
    "@types/tar": "6.1.13",
 | 
			
		||||
    "@types/ua-parser-js": "0.7.39",
 | 
			
		||||
    "@types/webspeechapi": "0.0.29",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "7.18.0",
 | 
			
		||||
    "@typescript-eslint/parser": "7.18.0",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "7.7.0",
 | 
			
		||||
    "@typescript-eslint/parser": "7.7.0",
 | 
			
		||||
    "@web/dev-server": "0.1.38",
 | 
			
		||||
    "@web/dev-server-rollup": "0.4.1",
 | 
			
		||||
    "babel-loader": "9.1.3",
 | 
			
		||||
    "babel-plugin-template-html-minifier": "4.1.0",
 | 
			
		||||
    "browserslist-useragent-regexp": "4.1.3",
 | 
			
		||||
    "chai": "5.1.1",
 | 
			
		||||
    "chai": "5.1.0",
 | 
			
		||||
    "del": "7.1.0",
 | 
			
		||||
    "eslint": "8.57.0",
 | 
			
		||||
    "eslint-config-airbnb-base": "15.0.0",
 | 
			
		||||
    "eslint-config-airbnb-typescript": "18.0.0",
 | 
			
		||||
    "eslint-config-prettier": "9.1.0",
 | 
			
		||||
    "eslint-import-resolver-webpack": "0.13.8",
 | 
			
		||||
    "eslint-plugin-disable": "2.0.3",
 | 
			
		||||
    "eslint-plugin-import": "2.29.1",
 | 
			
		||||
    "eslint-plugin-lit": "1.14.0",
 | 
			
		||||
    "eslint-plugin-lit-a11y": "4.1.4",
 | 
			
		||||
    "eslint-plugin-unused-imports": "4.0.1",
 | 
			
		||||
    "eslint-plugin-lit": "1.11.0",
 | 
			
		||||
    "eslint-plugin-lit-a11y": "4.1.2",
 | 
			
		||||
    "eslint-plugin-unused-imports": "3.1.0",
 | 
			
		||||
    "eslint-plugin-wc": "2.1.0",
 | 
			
		||||
    "fancy-log": "2.0.0",
 | 
			
		||||
    "fs-extra": "11.2.0",
 | 
			
		||||
    "glob": "11.0.0",
 | 
			
		||||
    "gulp": "5.0.0",
 | 
			
		||||
    "gulp-brotli": "3.0.0",
 | 
			
		||||
    "glob": "10.3.12",
 | 
			
		||||
    "gulp": "4.0.2",
 | 
			
		||||
    "gulp-json-transform": "0.5.0",
 | 
			
		||||
    "gulp-merge-json": "2.2.1",
 | 
			
		||||
    "gulp-rename": "2.0.0",
 | 
			
		||||
    "gulp-zopfli-green": "6.0.2",
 | 
			
		||||
    "gulp-zopfli-green": "6.0.1",
 | 
			
		||||
    "html-minifier-terser": "7.2.0",
 | 
			
		||||
    "husky": "9.1.4",
 | 
			
		||||
    "husky": "9.0.11",
 | 
			
		||||
    "instant-mocha": "1.5.2",
 | 
			
		||||
    "jszip": "3.10.1",
 | 
			
		||||
    "lint-staged": "15.2.8",
 | 
			
		||||
    "lint-staged": "15.2.2",
 | 
			
		||||
    "lit-analyzer": "2.0.3",
 | 
			
		||||
    "lodash.merge": "4.6.2",
 | 
			
		||||
    "lodash.template": "4.5.0",
 | 
			
		||||
    "magic-string": "0.30.11",
 | 
			
		||||
    "magic-string": "0.30.10",
 | 
			
		||||
    "map-stream": "0.0.7",
 | 
			
		||||
    "mocha": "10.5.0",
 | 
			
		||||
    "mocha": "10.4.0",
 | 
			
		||||
    "object-hash": "3.0.0",
 | 
			
		||||
    "open": "10.1.0",
 | 
			
		||||
    "pinst": "3.0.0",
 | 
			
		||||
    "prettier": "3.3.3",
 | 
			
		||||
    "prettier": "3.2.5",
 | 
			
		||||
    "rollup": "2.79.1",
 | 
			
		||||
    "rollup-plugin-string": "3.0.0",
 | 
			
		||||
    "rollup-plugin-terser": "7.0.2",
 | 
			
		||||
    "rollup-plugin-visualizer": "5.12.0",
 | 
			
		||||
    "serve-handler": "6.1.5",
 | 
			
		||||
    "sinon": "18.0.0",
 | 
			
		||||
    "systemjs": "6.15.1",
 | 
			
		||||
    "tar": "7.4.3",
 | 
			
		||||
    "sinon": "17.0.1",
 | 
			
		||||
    "source-map-url": "0.4.1",
 | 
			
		||||
    "systemjs": "6.14.3",
 | 
			
		||||
    "tar": "7.0.1",
 | 
			
		||||
    "terser-webpack-plugin": "5.3.10",
 | 
			
		||||
    "transform-async-modules-webpack-plugin": "1.1.1",
 | 
			
		||||
    "transform-async-modules-webpack-plugin": "1.1.0",
 | 
			
		||||
    "ts-lit-plugin": "2.0.2",
 | 
			
		||||
    "typescript": "5.5.4",
 | 
			
		||||
    "webpack": "5.93.0",
 | 
			
		||||
    "typescript": "5.4.5",
 | 
			
		||||
    "webpack": "5.91.0",
 | 
			
		||||
    "webpack-cli": "5.1.4",
 | 
			
		||||
    "webpack-dev-server": "5.0.4",
 | 
			
		||||
    "webpack-manifest-plugin": "5.0.0",
 | 
			
		||||
    "webpack-stats-plugin": "1.1.3",
 | 
			
		||||
    "webpackbar": "6.0.1",
 | 
			
		||||
    "workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
 | 
			
		||||
    "workbox-build": "7.0.0"
 | 
			
		||||
  },
 | 
			
		||||
  "_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch",
 | 
			
		||||
  "resolutions": {
 | 
			
		||||
@@ -254,9 +254,8 @@
 | 
			
		||||
    "lit": "2.8.0",
 | 
			
		||||
    "clean-css": "5.3.3",
 | 
			
		||||
    "@lit/reactive-element": "1.6.3",
 | 
			
		||||
    "@fullcalendar/daygrid": "6.1.15",
 | 
			
		||||
    "sortablejs@1.15.2": "patch:sortablejs@npm%3A1.15.2#~/.yarn/patches/sortablejs-npm-1.15.2-73347ae85a.patch",
 | 
			
		||||
    "leaflet-draw@1.0.4": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch"
 | 
			
		||||
  },
 | 
			
		||||
  "packageManager": "yarn@4.4.0"
 | 
			
		||||
  "packageManager": "yarn@4.1.1"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
		 Before Width: | Height: | Size: 5.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								public/static/images/logo_twitter.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/static/images/logo_twitter.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.2 KiB  | 
@@ -1,3 +0,0 @@
 | 
			
		||||
<svg width="1200" height="1227" viewBox="0 0 1200 1227" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<path d="M714.163 519.284L1160.89 0H1055.03L667.137 450.887L357.328 0H0L468.492 681.821L0 1226.37H105.866L515.491 750.218L842.672 1226.37H1200L714.137 519.284H714.163ZM569.165 687.828L521.697 619.934L144.011 79.6944H306.615L611.412 515.685L658.88 583.579L1055.08 1150.3H892.476L569.165 687.854V687.828Z" fill="white"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 430 B  | 
@@ -1,66 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<svg id="b" data-name="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760.69 138.69">
 | 
			
		||||
  <g id="c" data-name="Layer_1">
 | 
			
		||||
    <g>
 | 
			
		||||
      <g>
 | 
			
		||||
        <path d="M136.22,68.35c3.31-.05,6.29.72,8.92,2.31s4.67,3.77,6.11,6.55,2.14,5.89,2.11,9.33c.03,3.44-.68,6.55-2.12,9.34s-3.49,4.98-6.13,6.56-5.62,2.36-8.93,2.31c-3.33,0-6.29-.78-8.88-2.33s-4.6-3.71-6.02-6.48-2.13-5.9-2.13-9.4c-.03-2.56.38-4.98,1.22-7.24s2.01-4.21,3.5-5.82,3.31-2.88,5.45-3.8,4.45-1.36,6.91-1.32ZM136.27,98.35c3.09,0,5.56-1.07,7.41-3.2s2.77-5,2.77-8.61-.91-6.52-2.73-8.65-4.3-3.19-7.44-3.19-5.63,1.06-7.46,3.19-2.75,5.01-2.75,8.65.91,6.52,2.74,8.64,4.32,3.18,7.48,3.18Z"/>
 | 
			
		||||
        <path d="M184.16,80.53c0,3.47-1.06,6.27-3.18,8.41s-4.98,3.21-8.59,3.21h-7.45v12h-6.56v-35.18h14.06c3.64,0,6.5,1.04,8.59,3.11s3.13,4.89,3.13,8.45ZM177.25,80.39c0-1.64-.52-2.98-1.56-4.03s-2.52-1.57-4.44-1.57h-6.3v11.65h6.26c1.95,0,3.45-.55,4.49-1.65s1.56-2.57,1.56-4.39Z"/>
 | 
			
		||||
        <path d="M210.82,98.02v6.14h-22.03v-35.18h21.98v6.19h-15.42v8.3h13.78v5.81h-13.78v8.74h15.47Z"/>
 | 
			
		||||
        <path d="M246.95,68.98v35.18h-6.49l-16.08-24.77v24.77h-6.52v-35.18h6.52l16.08,24.77v-24.77h6.49Z"/>
 | 
			
		||||
        <path d="M266.45,68.98h6.56v14.44l14.7.05v-14.48h6.63v35.18h-6.63v-14.84l-14.7-.09v14.93h-6.56v-35.18Z"/>
 | 
			
		||||
        <path d="M316.41,68.35c3.31-.05,6.29.72,8.92,2.31s4.67,3.77,6.11,6.55,2.14,5.89,2.11,9.33c.03,3.44-.68,6.55-2.12,9.34s-3.49,4.98-6.13,6.56-5.62,2.36-8.93,2.31c-3.33,0-6.29-.78-8.88-2.33s-4.6-3.71-6.02-6.48-2.13-5.9-2.13-9.4c-.03-2.56.38-4.98,1.22-7.24s2.01-4.21,3.5-5.82,3.31-2.88,5.45-3.8,4.45-1.36,6.91-1.32ZM316.46,98.35c3.09,0,5.56-1.07,7.41-3.2s2.77-5,2.77-8.61-.91-6.52-2.73-8.65-4.3-3.19-7.44-3.19-5.63,1.06-7.46,3.19-2.75,5.01-2.75,8.65.91,6.52,2.74,8.64,4.32,3.18,7.48,3.18Z"/>
 | 
			
		||||
        <path d="M373.66,68.98v35.18h-6.45v-20.55l-8.11,20.55h-6.23l-8.02-20.39v20.39h-6.28v-35.18h6.28l11.13,27.54,11.23-27.54h6.45Z"/>
 | 
			
		||||
        <path d="M402.87,98.02v6.14h-22.03v-35.18h21.98v6.19h-15.42v8.3h13.78v5.81h-13.78v8.74h15.47Z"/>
 | 
			
		||||
        <path d="M427.83,75.12v8.93h13.01l-.05,5.91h-12.96v14.2h-6.52v-35.18h21.98l-.05,6.14h-15.42Z"/>
 | 
			
		||||
        <path d="M463.16,68.35c3.31-.05,6.29.72,8.92,2.31s4.67,3.77,6.11,6.55,2.14,5.89,2.11,9.33c.03,3.44-.68,6.55-2.12,9.34s-3.49,4.98-6.13,6.56-5.62,2.36-8.93,2.31c-3.33,0-6.29-.78-8.88-2.33s-4.6-3.71-6.02-6.48-2.13-5.9-2.13-9.4c-.03-2.56.38-4.98,1.22-7.24s2.01-4.21,3.5-5.82,3.31-2.88,5.45-3.8,4.45-1.36,6.91-1.32ZM463.21,98.35c3.09,0,5.56-1.07,7.41-3.2s2.77-5,2.77-8.61-.91-6.52-2.73-8.65-4.3-3.19-7.44-3.19-5.63,1.06-7.46,3.19-2.75,5.01-2.75,8.65.91,6.52,2.74,8.64,4.32,3.18,7.48,3.18Z"/>
 | 
			
		||||
        <path d="M485,68.98h6.56v22.12c0,2.31.72,4.12,2.16,5.43s3.3,1.96,5.58,1.96,4.08-.67,5.58-2.02,2.25-3.13,2.25-5.37v-22.12h6.52v22.31c0,2.08-.38,3.98-1.14,5.7s-1.79,3.14-3.09,4.25-2.82,1.98-4.56,2.59-3.59.91-5.55.91c-2.59,0-4.96-.52-7.1-1.55s-3.88-2.58-5.2-4.65-1.99-4.49-1.99-7.25v-22.31Z"/>
 | 
			
		||||
        <path d="M549.63,68.98v35.18h-6.49l-16.08-24.77v24.77h-6.52v-35.18h6.52l16.08,24.77v-24.77h6.49Z"/>
 | 
			
		||||
        <path d="M586.9,86.58c.05,3.34-.71,6.37-2.27,9.08s-3.7,4.82-6.42,6.32-5.73,2.23-9.02,2.18h-12.42v-35.18h12.42c2.45-.03,4.78.39,6.98,1.28s4.1,2.1,5.68,3.66,2.84,3.43,3.75,5.64,1.35,4.55,1.3,7.03ZM579.99,86.58c0-3.39-1-6.16-3.01-8.3s-4.62-3.21-7.84-3.21h-5.81v23.04h5.81c3.27,0,5.89-1.06,7.88-3.19s2.98-4.91,2.98-8.34Z"/>
 | 
			
		||||
        <path d="M609.16,96.19h-12.73l-2.79,7.97h-6.82l12.68-35.18h6.63l12.66,35.18h-6.96l-2.67-7.97ZM607.24,90.73l-4.43-12.87-4.45,12.87h8.88Z"/>
 | 
			
		||||
        <path d="M642.87,75.17h-9.89v28.99h-6.56v-28.99h-9.94v-6.19h26.39v6.19Z"/>
 | 
			
		||||
        <path d="M647.06,104.16v-35.18h6.56v35.18h-6.56Z"/>
 | 
			
		||||
        <path d="M675.71,68.35c3.31-.05,6.29.72,8.92,2.31s4.67,3.77,6.11,6.55,2.14,5.89,2.11,9.33c.03,3.44-.68,6.55-2.12,9.34s-3.49,4.98-6.13,6.56-5.62,2.36-8.93,2.31c-3.33,0-6.29-.78-8.88-2.33s-4.6-3.71-6.02-6.48-2.13-5.9-2.13-9.4c-.03-2.56.38-4.98,1.22-7.24s2.01-4.21,3.5-5.82,3.31-2.88,5.45-3.8,4.45-1.36,6.91-1.32ZM675.76,98.35c3.09,0,5.56-1.07,7.41-3.2s2.77-5,2.77-8.61-.91-6.52-2.73-8.65-4.3-3.19-7.44-3.19-5.63,1.06-7.46,3.19-2.75,5.01-2.75,8.65.91,6.52,2.74,8.64,4.32,3.18,7.48,3.18Z"/>
 | 
			
		||||
        <path d="M726.96,68.98v35.18h-6.49l-16.08-24.77v24.77h-6.52v-35.18h6.52l16.08,24.77v-24.77h6.49Z"/>
 | 
			
		||||
      </g>
 | 
			
		||||
      <g>
 | 
			
		||||
        <path d="M94.34,79.34c0,2.75-2.25,5-5,5h-50c-2.75,0-5-2.25-5-5v-20c0-2.75,1.59-6.59,3.54-8.54l22.93-22.93c1.94-1.94,5.13-1.94,7.07,0l22.93,22.93c1.94,1.94,3.54,5.79,3.54,8.54v20Z"/>
 | 
			
		||||
        <g>
 | 
			
		||||
          <rect x="34.34" y="94.34" width="60" height="10" rx="2.5" ry="2.5"/>
 | 
			
		||||
          <rect x="34.34" y="94.34" width="10" height="20" rx="1.56" ry="1.56"/>
 | 
			
		||||
          <rect x="84.34" y="94.34" width="10" height="20" rx="1.56" ry="1.56"/>
 | 
			
		||||
        </g>
 | 
			
		||||
      </g>
 | 
			
		||||
      <path d="M735.34,3c12.32,0,22.34,10.02,22.34,22.34v88c0,12.32-10.02,22.34-22.34,22.34H25.34c-12.32,0-22.34-10.02-22.34-22.34V25.34C3,13.02,13.02,3,25.34,3h710M735.34,0H25.34C11.37,0,0,11.37,0,25.34v88c0,13.98,11.37,25.34,25.34,25.34h710c13.97,0,25.34-11.37,25.34-25.34V25.34c0-13.98-11.37-25.34-25.34-25.34h0Z"/>
 | 
			
		||||
      <g>
 | 
			
		||||
        <path d="M120.98,36.79h2.95v7.26l7.66.02v-7.29h2.97v17.37h-2.97v-7.47l-7.66-.02v7.49h-2.95v-17.37Z"/>
 | 
			
		||||
        <path d="M146.97,36.47c1.63,0,3.09.39,4.37,1.16s2.28,1.84,2.99,3.2,1.06,2.9,1.06,4.61c.02,1.7-.32,3.24-1.04,4.62s-1.72,2.47-3.02,3.25-2.75,1.16-4.36,1.14c-1.62.02-3.08-.36-4.37-1.14s-2.29-1.86-3-3.24-1.05-2.91-1.03-4.61c0-1.27.2-2.47.61-3.58s.99-2.08,1.72-2.88,1.63-1.42,2.68-1.88,2.18-.67,3.39-.66ZM146.99,51.57c1.6,0,2.89-.56,3.85-1.67s1.45-2.6,1.45-4.45-.48-3.32-1.45-4.43-2.25-1.66-3.85-1.66-2.89.55-3.86,1.66-1.45,2.58-1.45,4.43.48,3.34,1.44,4.46,2.25,1.67,3.88,1.67Z"/>
 | 
			
		||||
        <path d="M176.51,36.79v17.37h-2.89v-10.78l-4.29,10.78h-2.81l-4.25-10.71v10.71h-2.84v-17.37h2.84l5.66,13.92,5.69-13.92h2.89Z"/>
 | 
			
		||||
        <path d="M192.41,51.37v2.79h-10.78v-17.37h10.78v2.81h-7.83v4.5h7v2.61h-7v4.66h7.83Z"/>
 | 
			
		||||
        <path d="M213.93,50.11h-6.61l-1.43,4.04h-3.04l6.27-17.37h3.04l6.29,17.37h-3.11l-1.41-4.04ZM213.07,47.62l-2.43-6.95-2.45,6.95h4.88Z"/>
 | 
			
		||||
        <path d="M226.96,36.47c1.59,0,2.91.39,3.96,1.16s1.7,1.81,1.94,3.1l-2.78.76c-.16-.74-.52-1.32-1.09-1.72s-1.27-.6-2.11-.6c-.9,0-1.61.21-2.14.64s-.79,1-.79,1.71c0,1.12.7,1.85,2.09,2.18l2.84.71c1.46.38,2.56.98,3.29,1.81s1.09,1.84,1.09,3.05c0,1.55-.56,2.8-1.68,3.76s-2.63,1.44-4.51,1.44c-1.7,0-3.13-.4-4.3-1.2-1.15-.83-1.84-1.92-2.05-3.28l2.78-.72c.1.77.48,1.37,1.14,1.8s1.5.65,2.53.65,1.76-.21,2.32-.62.84-.98.84-1.69c0-1.12-.7-1.85-2.09-2.21l-2.84-.69c-1.46-.33-2.55-.92-3.28-1.77s-1.1-1.88-1.1-3.11c0-1.53.54-2.78,1.63-3.74s2.52-1.44,4.29-1.44Z"/>
 | 
			
		||||
        <path d="M242.38,36.47c1.59,0,2.91.39,3.96,1.16s1.7,1.81,1.94,3.1l-2.78.76c-.16-.74-.52-1.32-1.09-1.72s-1.27-.6-2.11-.6c-.9,0-1.61.21-2.14.64s-.79,1-.79,1.71c0,1.12.7,1.85,2.09,2.18l2.84.71c1.46.38,2.56.98,3.29,1.81s1.09,1.84,1.09,3.05c0,1.55-.56,2.8-1.68,3.76s-2.63,1.44-4.51,1.44c-1.7,0-3.13-.4-4.3-1.2-1.15-.83-1.84-1.92-2.05-3.28l2.78-.72c.1.77.48,1.37,1.14,1.8s1.5.65,2.53.65,1.76-.21,2.32-.62.84-.98.84-1.69c0-1.12-.7-1.85-2.09-2.21l-2.84-.69c-1.46-.33-2.55-.92-3.28-1.77s-1.1-1.88-1.1-3.11c0-1.53.54-2.78,1.63-3.74s2.52-1.44,4.29-1.44Z"/>
 | 
			
		||||
        <path d="M252.68,54.16v-17.37h2.95v17.37h-2.95Z"/>
 | 
			
		||||
        <path d="M265.82,36.47c1.59,0,2.91.39,3.96,1.16s1.7,1.81,1.94,3.1l-2.78.76c-.16-.74-.52-1.32-1.09-1.72s-1.27-.6-2.11-.6c-.9,0-1.61.21-2.14.64s-.79,1-.79,1.71c0,1.12.7,1.85,2.09,2.18l2.84.71c1.46.38,2.56.98,3.29,1.81s1.09,1.84,1.09,3.05c0,1.55-.56,2.8-1.68,3.76s-2.63,1.44-4.51,1.44c-1.7,0-3.13-.4-4.3-1.2-1.15-.83-1.84-1.92-2.05-3.28l2.78-.72c.1.77.48,1.37,1.14,1.8s1.5.65,2.53.65,1.76-.21,2.32-.62.84-.98.84-1.69c0-1.12-.7-1.85-2.09-2.21l-2.84-.69c-1.46-.33-2.55-.92-3.28-1.77s-1.1-1.88-1.1-3.11c0-1.53.54-2.78,1.63-3.74s2.52-1.44,4.29-1.44Z"/>
 | 
			
		||||
        <path d="M287.47,39.57h-4.97v14.58h-2.95v-14.58h-4.97v-2.79h12.9v2.79Z"/>
 | 
			
		||||
        <path d="M298.87,50.11h-6.61l-1.43,4.04h-3.04l6.27-17.37h3.04l6.29,17.37h-3.11l-1.41-4.04ZM298.01,47.62l-2.43-6.95-2.45,6.95h4.88Z"/>
 | 
			
		||||
        <path d="M320.89,36.79v17.37h-2.93l-8.25-12.67v12.67h-2.93v-17.37h2.93l8.25,12.65v-12.65h2.93Z"/>
 | 
			
		||||
        <path d="M337.31,39.57h-4.97v14.58h-2.95v-14.58h-4.97v-2.79h12.9v2.79Z"/>
 | 
			
		||||
        <path d="M348.75,54.16v-17.14h2.05v17.14h-2.05Z"/>
 | 
			
		||||
        <path d="M360.95,36.72c1.55,0,2.82.38,3.81,1.14,1,.74,1.61,1.72,1.82,2.95l-1.95.52c-.16-.87-.56-1.54-1.23-2.02s-1.5-.71-2.5-.71c-1.08,0-1.95.27-2.6.8s-.97,1.24-.97,2.13c0,1.36.84,2.26,2.52,2.71l2.9.73c1.37.34,2.41.9,3.11,1.68s1.05,1.74,1.05,2.9c0,1.46-.53,2.64-1.6,3.54s-2.49,1.36-4.28,1.36c-1.61,0-2.95-.37-4.03-1.12s-1.72-1.76-1.95-3.06l1.98-.55c.13.88.55,1.56,1.25,2.06s1.63.75,2.77.75,2.12-.26,2.79-.77,1.02-1.22,1.02-2.14c0-1.44-.84-2.36-2.52-2.77l-2.88-.71c-1.38-.34-2.42-.9-3.12-1.69s-1.05-1.75-1.05-2.9c0-1.44.52-2.61,1.56-3.51s2.4-1.35,4.09-1.35Z"/>
 | 
			
		||||
        <path d="M388.35,49.75h-7.54l-1.59,4.4h-2.07l6.25-17.14h2.36l6.31,17.14h-2.15l-1.57-4.4ZM387.73,48.05l-3.09-8.71-3.2,8.71h6.29Z"/>
 | 
			
		||||
        <path d="M415.46,42.47c0,1.6-.5,2.91-1.5,3.95s-2.32,1.56-3.97,1.56h-4.53v6.18h-2.05v-17.14h6.6c1.67,0,3,.49,3.98,1.47s1.47,2.31,1.47,3.98ZM413.31,42.42c0-1.07-.32-1.92-.95-2.56s-1.51-.96-2.64-.96h-4.26v7.24h4.17c1.15,0,2.06-.34,2.71-1.02s.98-1.58.98-2.7Z"/>
 | 
			
		||||
        <path d="M428.37,46.9l3.43,7.26h-2.31l-3.18-6.95h-4.76v6.95h-2.05v-17.14h6.54c1.81,0,3.22.45,4.24,1.35s1.53,2.14,1.53,3.72c0,1.22-.3,2.26-.9,3.1s-1.44,1.41-2.53,1.7ZM429.64,42.12c0-1.01-.32-1.81-.95-2.38s-1.52-.86-2.66-.86h-4.5v6.47h4.53c1.15,0,2.03-.28,2.64-.85s.92-1.36.92-2.38Z"/>
 | 
			
		||||
        <path d="M443.34,36.74c1.18-.02,2.28.2,3.31.65s1.9,1.07,2.62,1.85,1.28,1.73,1.69,2.83.6,2.27.59,3.52c.02,1.67-.33,3.19-1.03,4.54s-1.68,2.42-2.95,3.19-2.68,1.14-4.25,1.12c-1.59,0-3-.38-4.25-1.13s-2.21-1.81-2.89-3.15-1.03-2.87-1.03-4.57c-.02-1.67.32-3.18,1.02-4.53s1.67-2.42,2.93-3.2,2.68-1.15,4.24-1.13ZM443.34,52.45c1.8,0,3.26-.64,4.38-1.91s1.68-2.92,1.68-4.95-.56-3.71-1.68-4.98-2.58-1.9-4.38-1.9-3.28.63-4.4,1.9-1.68,2.93-1.68,4.98.56,3.69,1.68,4.96,2.59,1.9,4.39,1.9Z"/>
 | 
			
		||||
        <path d="M464.3,37.02v12.42c0,1.49-.47,2.71-1.41,3.64s-2.17,1.39-3.71,1.39c-1.56,0-2.76-.46-3.61-1.37s-1.27-2.13-1.27-3.69v-.55h1.98v.55c0,1.18.29,1.99.86,2.45.59.45,1.26.67,2.02.67.93,0,1.68-.26,2.24-.78.57-.52.85-1.32.85-2.39v-12.35h2.05Z"/>
 | 
			
		||||
        <path d="M479.86,52.23v1.93h-10.35v-17.14h10.33v1.95h-8.31v5.67h7.53v1.8h-7.53v5.79h8.33Z"/>
 | 
			
		||||
        <path d="M496.97,42.42c-.36-1.15-1.01-2.06-1.93-2.71s-2.02-.98-3.3-.98c-1.79,0-3.24.63-4.35,1.89s-1.66,2.92-1.66,4.96.55,3.72,1.66,4.96,2.55,1.86,4.33,1.86c1.27,0,2.39-.31,3.35-.94.95-.63,1.61-1.44,1.98-2.45l1.93.83c-.55,1.41-1.48,2.53-2.78,3.36-1.31.81-2.81,1.22-4.51,1.22-2.4,0-4.35-.81-5.84-2.43s-2.24-3.75-2.24-6.4c0-1.74.34-3.28,1.02-4.62s1.64-2.39,2.88-3.13,2.65-1.11,4.25-1.11c1.8,0,3.33.46,4.59,1.38,1.26.91,2.12,2.1,2.57,3.59l-1.93.71Z"/>
 | 
			
		||||
        <path d="M512.92,38.95h-5.12v15.21h-2.05v-15.21h-5.16v-1.93h12.33v1.93Z"/>
 | 
			
		||||
        <path d="M536.4,49.66c0,1.39-.49,2.48-1.46,3.29s-2.27,1.21-3.9,1.21h-6.72v-17.12h6.58c1.65,0,2.95.41,3.89,1.24s1.41,1.93,1.41,3.32c0,.92-.21,1.72-.64,2.4s-1.02,1.2-1.79,1.57c.81.37,1.45.91,1.92,1.62s.7,1.53.7,2.47ZM526.3,38.81v5.97h4.54c1.03,0,1.85-.27,2.46-.81s.92-1.24.92-2.09c0-.91-.31-1.65-.93-2.22s-1.45-.85-2.5-.85h-4.5ZM534.45,49.39c0-.91-.32-1.64-.95-2.17s-1.44-.8-2.46-.8h-4.74v5.95h4.74c1.04,0,1.86-.27,2.48-.81s.92-1.26.92-2.16Z"/>
 | 
			
		||||
        <path d="M541.07,37.02l4.54,8.1,4.54-8.1h2.24l-5.76,10.05v7.09h-2.05v-7.09l-5.83-10.05h2.31Z"/>
 | 
			
		||||
        <path d="M574.27,38.95h-5.12v15.21h-2.05v-15.21h-5.16v-1.93h12.33v1.93Z"/>
 | 
			
		||||
        <path d="M577.95,37.02h2.05v7.55l8.74.02v-7.58h2.05v17.14h-2.05v-7.76l-8.74-.02v7.79h-2.05v-17.14Z"/>
 | 
			
		||||
        <path d="M606.55,52.23v1.93h-10.35v-17.14h10.33v1.95h-8.31v5.67h7.53v1.8h-7.53v5.79h8.33Z"/>
 | 
			
		||||
      </g>
 | 
			
		||||
    </g>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 12 KiB  | 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 | 
			
		||||
 | 
			
		||||
[project]
 | 
			
		||||
name         = "home-assistant-frontend"
 | 
			
		||||
version      = "20240809.0"
 | 
			
		||||
version      = "20240424.0"
 | 
			
		||||
license      = {text = "Apache-2.0"}
 | 
			
		||||
description  = "The Home Assistant frontend"
 | 
			
		||||
readme       = "README.md"
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,8 @@
 | 
			
		||||
    {
 | 
			
		||||
      "description": "Group tsparticles engine and presets",
 | 
			
		||||
      "groupName": "tsparticles",
 | 
			
		||||
      "matchPackageNames": ["tsparticles-engine", "tsparticles-preset-{/,}**"]
 | 
			
		||||
      "matchPackageNames": ["tsparticles-engine"],
 | 
			
		||||
      "matchPackagePrefixes": ["tsparticles-preset-"]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "description": "Group date-fns with dependent timezone package",
 | 
			
		||||
@@ -47,8 +48,8 @@
 | 
			
		||||
    {
 | 
			
		||||
      "description": "Group and temporarily disable WDS packages",
 | 
			
		||||
      "groupName": "Web Dev Server",
 | 
			
		||||
      "enabled": false,
 | 
			
		||||
      "matchPackageNames": ["@web/dev-server{/,}**"]
 | 
			
		||||
      "matchPackagePrefixes": ["@web/dev-server"],
 | 
			
		||||
      "enabled": false
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,6 @@ import {
 | 
			
		||||
  mdiFormatListBulleted,
 | 
			
		||||
  mdiFormatListCheckbox,
 | 
			
		||||
  mdiFormTextbox,
 | 
			
		||||
  mdiForumOutline,
 | 
			
		||||
  mdiGauge,
 | 
			
		||||
  mdiGoogleAssistant,
 | 
			
		||||
  mdiGoogleCirclesCommunities,
 | 
			
		||||
@@ -99,7 +98,7 @@ export const FIXED_DOMAIN_ICONS = {
 | 
			
		||||
  calendar: mdiCalendar,
 | 
			
		||||
  climate: mdiThermostat,
 | 
			
		||||
  configurator: mdiCog,
 | 
			
		||||
  conversation: mdiForumOutline,
 | 
			
		||||
  conversation: mdiMicrophoneMessage,
 | 
			
		||||
  counter: mdiCounter,
 | 
			
		||||
  date: mdiCalendar,
 | 
			
		||||
  datetime: mdiCalendarClock,
 | 
			
		||||
@@ -236,8 +235,6 @@ export const SENSOR_ENTITIES = [
 | 
			
		||||
  "weather",
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export const ASSIST_ENTITIES = ["conversation", "stt", "tts"];
 | 
			
		||||
 | 
			
		||||
/** Domains that render an input element instead of a text value when displayed in a row.
 | 
			
		||||
 *  Those rows should then not show a cursor pointer when hovered (which would normally
 | 
			
		||||
 *  be the default) unless the element itself enforces it (e.g. a button). Also those elements
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
import { getWeekStartByLocale } from "weekstart";
 | 
			
		||||
import { FrontendLocaleData, FirstWeekday } from "../../data/translation";
 | 
			
		||||
 | 
			
		||||
import "../../resources/intl-polyfill";
 | 
			
		||||
 | 
			
		||||
export const weekdays = [
 | 
			
		||||
  "sunday",
 | 
			
		||||
  "monday",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import { HassConfig } from "home-assistant-js-websocket";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { DateFormat, FrontendLocaleData } from "../../data/translation";
 | 
			
		||||
import "../../resources/intl-polyfill";
 | 
			
		||||
import { resolveTimeZone } from "./resolve-time-zone";
 | 
			
		||||
 | 
			
		||||
// Tuesday, August 10
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import { HassConfig } from "home-assistant-js-websocket";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { FrontendLocaleData } from "../../data/translation";
 | 
			
		||||
import "../../resources/intl-polyfill";
 | 
			
		||||
import { formatDateNumeric } from "./format_date";
 | 
			
		||||
import { formatTime } from "./format_time";
 | 
			
		||||
import { resolveTimeZone } from "./resolve-time-zone";
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import { HaDurationData } from "../../components/ha-duration-input";
 | 
			
		||||
import { FrontendLocaleData } from "../../data/translation";
 | 
			
		||||
import "../../resources/intl-polyfill";
 | 
			
		||||
 | 
			
		||||
const leftPad = (num: number) => (num < 10 ? `0${num}` : num);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import { HassConfig } from "home-assistant-js-websocket";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { FrontendLocaleData } from "../../data/translation";
 | 
			
		||||
import "../../resources/intl-polyfill";
 | 
			
		||||
import { resolveTimeZone } from "./resolve-time-zone";
 | 
			
		||||
import { useAmPm } from "./use_am_pm";
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import "../../resources/intl-polyfill";
 | 
			
		||||
 | 
			
		||||
export const localizeWeekdays = memoizeOne(
 | 
			
		||||
  (language: string, short: boolean): string[] => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { FrontendLocaleData } from "../../data/translation";
 | 
			
		||||
import "../../resources/intl-polyfill";
 | 
			
		||||
import { selectUnit } from "../util/select-unit";
 | 
			
		||||
 | 
			
		||||
const formatRelTimeMem = memoizeOne(
 | 
			
		||||
 
 | 
			
		||||
@@ -108,8 +108,6 @@ export const storage =
 | 
			
		||||
    subscribe?: boolean;
 | 
			
		||||
    state?: boolean;
 | 
			
		||||
    stateOptions?: InternalPropertyDeclaration;
 | 
			
		||||
    serializer?: (value: any) => any;
 | 
			
		||||
    deserializer?: (value: any) => any;
 | 
			
		||||
  }): any =>
 | 
			
		||||
  (clsElement: ClassElement) => {
 | 
			
		||||
    const storageName = options.storage || "localStorage";
 | 
			
		||||
@@ -143,9 +141,7 @@ export const storage =
 | 
			
		||||
 | 
			
		||||
    const getValue = (): any =>
 | 
			
		||||
      storageInstance.hasKey(storageKey!)
 | 
			
		||||
        ? options.deserializer
 | 
			
		||||
          ? options.deserializer(storageInstance.getValue(storageKey!))
 | 
			
		||||
          : storageInstance.getValue(storageKey!)
 | 
			
		||||
        ? storageInstance.getValue(storageKey!)
 | 
			
		||||
        : initVal;
 | 
			
		||||
 | 
			
		||||
    const setValue = (el: ReactiveElement, value: any) => {
 | 
			
		||||
@@ -153,10 +149,7 @@ export const storage =
 | 
			
		||||
      if (options.state) {
 | 
			
		||||
        oldValue = getValue();
 | 
			
		||||
      }
 | 
			
		||||
      storageInstance.setValue(
 | 
			
		||||
        storageKey!,
 | 
			
		||||
        options.serializer ? options.serializer(value) : value
 | 
			
		||||
      );
 | 
			
		||||
      storageInstance.setValue(storageKey!, value);
 | 
			
		||||
      if (options.state) {
 | 
			
		||||
        el.requestUpdate(clsElement.key, oldValue);
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
export type MediaQueriesListener = () => void;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Attach a media query. Listener is called right away and when it matches.
 | 
			
		||||
 * @param mediaQuery media query to match.
 | 
			
		||||
@@ -9,7 +7,7 @@ export type MediaQueriesListener = () => void;
 | 
			
		||||
export const listenMediaQuery = (
 | 
			
		||||
  mediaQuery: string,
 | 
			
		||||
  matchesChanged: (matches: boolean) => void
 | 
			
		||||
): MediaQueriesListener => {
 | 
			
		||||
) => {
 | 
			
		||||
  const mql = matchMedia(mediaQuery);
 | 
			
		||||
  const listener = (e) => matchesChanged(e.matches);
 | 
			
		||||
  mql.addListener(listener);
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
export const preventDefault = (ev) => ev.preventDefault();
 | 
			
		||||
@@ -19,11 +19,28 @@ import { blankBeforeUnit } from "../translations/blank_before_unit";
 | 
			
		||||
import { LocalizeFunc } from "../translations/localize";
 | 
			
		||||
import { computeDomain } from "./compute_domain";
 | 
			
		||||
 | 
			
		||||
export const computeStateDisplaySingleEntity = (
 | 
			
		||||
  localize: LocalizeFunc,
 | 
			
		||||
  stateObj: HassEntity,
 | 
			
		||||
  locale: FrontendLocaleData,
 | 
			
		||||
  config: HassConfig,
 | 
			
		||||
  entity: EntityRegistryDisplayEntry | undefined,
 | 
			
		||||
  state?: string
 | 
			
		||||
): string =>
 | 
			
		||||
  computeStateDisplayFromEntityAttributes(
 | 
			
		||||
    localize,
 | 
			
		||||
    locale,
 | 
			
		||||
    config,
 | 
			
		||||
    entity,
 | 
			
		||||
    stateObj.entity_id,
 | 
			
		||||
    stateObj.attributes,
 | 
			
		||||
    state !== undefined ? state : stateObj.state
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
export const computeStateDisplay = (
 | 
			
		||||
  localize: LocalizeFunc,
 | 
			
		||||
  stateObj: HassEntity,
 | 
			
		||||
  locale: FrontendLocaleData,
 | 
			
		||||
  sensorNumericDeviceClasses: string[],
 | 
			
		||||
  config: HassConfig,
 | 
			
		||||
  entities: HomeAssistant["entities"],
 | 
			
		||||
  state?: string
 | 
			
		||||
@@ -35,7 +52,6 @@ export const computeStateDisplay = (
 | 
			
		||||
  return computeStateDisplayFromEntityAttributes(
 | 
			
		||||
    localize,
 | 
			
		||||
    locale,
 | 
			
		||||
    sensorNumericDeviceClasses,
 | 
			
		||||
    config,
 | 
			
		||||
    entity,
 | 
			
		||||
    stateObj.entity_id,
 | 
			
		||||
@@ -47,7 +63,6 @@ export const computeStateDisplay = (
 | 
			
		||||
export const computeStateDisplayFromEntityAttributes = (
 | 
			
		||||
  localize: LocalizeFunc,
 | 
			
		||||
  locale: FrontendLocaleData,
 | 
			
		||||
  sensorNumericDeviceClasses: string[],
 | 
			
		||||
  config: HassConfig,
 | 
			
		||||
  entity: EntityRegistryDisplayEntry | undefined,
 | 
			
		||||
  entityId: string,
 | 
			
		||||
@@ -58,15 +73,8 @@ export const computeStateDisplayFromEntityAttributes = (
 | 
			
		||||
    return localize(`state.default.${state}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const domain = computeDomain(entityId);
 | 
			
		||||
 | 
			
		||||
  // Entities with a `unit_of_measurement` or `state_class` are numeric values and should use `formatNumber`
 | 
			
		||||
  if (
 | 
			
		||||
    isNumericFromAttributes(
 | 
			
		||||
      attributes,
 | 
			
		||||
      domain === "sensor" ? sensorNumericDeviceClasses : []
 | 
			
		||||
    )
 | 
			
		||||
  ) {
 | 
			
		||||
  if (isNumericFromAttributes(attributes)) {
 | 
			
		||||
    // state is duration
 | 
			
		||||
    if (
 | 
			
		||||
      attributes.device_class === "duration" &&
 | 
			
		||||
@@ -112,6 +120,8 @@ export const computeStateDisplayFromEntityAttributes = (
 | 
			
		||||
    return value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const domain = computeDomain(entityId);
 | 
			
		||||
 | 
			
		||||
  if (domain === "datetime") {
 | 
			
		||||
    const time = new Date(state);
 | 
			
		||||
    return formatDateTime(time, locale, config);
 | 
			
		||||
 
 | 
			
		||||
@@ -28,15 +28,7 @@ export const FIXED_DOMAIN_STATES = {
 | 
			
		||||
  input_button: [],
 | 
			
		||||
  lawn_mower: ["error", "paused", "mowing", "docked"],
 | 
			
		||||
  light: ["on", "off"],
 | 
			
		||||
  lock: [
 | 
			
		||||
    "jammed",
 | 
			
		||||
    "locked",
 | 
			
		||||
    "locking",
 | 
			
		||||
    "unlocked",
 | 
			
		||||
    "unlocking",
 | 
			
		||||
    "opening",
 | 
			
		||||
    "open",
 | 
			
		||||
  ],
 | 
			
		||||
  lock: ["jammed", "locked", "locking", "unlocked", "unlocking"],
 | 
			
		||||
  media_player: [
 | 
			
		||||
    "off",
 | 
			
		||||
    "on",
 | 
			
		||||
@@ -125,7 +117,6 @@ const FIXED_DOMAIN_ATTRIBUTE_STATES = {
 | 
			
		||||
      "off",
 | 
			
		||||
      "idle",
 | 
			
		||||
      "preheating",
 | 
			
		||||
      "defrosting",
 | 
			
		||||
      "heating",
 | 
			
		||||
      "cooling",
 | 
			
		||||
      "drying",
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user