mirror of
				https://github.com/home-assistant/frontend.git
				synced 2025-10-31 06:29:43 +00:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			combine-ap
			...
			selectors-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 4c2ca9224d | 
| @@ -4,7 +4,7 @@ | ||||
|     "dockerfile": "Dockerfile", | ||||
|     "context": ".." | ||||
|   }, | ||||
|   "appPort": "8124:8123", | ||||
|   "appPort": 8123, | ||||
|   "context": "..", | ||||
|   "postCreateCommand": "script/bootstrap", | ||||
|   "extensions": [ | ||||
| @@ -26,9 +26,6 @@ | ||||
|     "[typescript]": { | ||||
|       "editor.defaultFormatter": "esbenp.prettier-vscode" | ||||
|     }, | ||||
|     "[javascript]": { | ||||
|       "editor.defaultFormatter": "esbenp.prettier-vscode" | ||||
|     }, | ||||
|     "files.trimTrailingWhitespace": true | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -74,12 +74,12 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| ## Problem-relevant frontend configuration | ||||
| ## Problem-relevant configuration | ||||
| 
 | ||||
| <!-- | ||||
|   An example configuration that caused the problem for you, e.g. the YAML configuration | ||||
|   of the used cards. Fill this out even if it seems unimportant to you. Please be sure | ||||
|   to remove personal information like passwords, private URLs and other credentials. | ||||
|   An example configuration that caused the problem for you. Fill this out even | ||||
|   if it seems unimportant to you. Please be sure to remove personal information | ||||
|   like passwords, private URLs and other credentials. | ||||
| --> | ||||
| 
 | ||||
| ```yaml | ||||
| @@ -89,7 +89,7 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w | ||||
| ## Javascript errors shown in your browser console/inspector | ||||
| 
 | ||||
| <!-- | ||||
|   If you come across any Javascript or other error logs, e.g. in your browser | ||||
|   If you come across any javascript or other error logs, e.g., in your browser | ||||
|   console/inspector please provide them. | ||||
| --> | ||||
| 
 | ||||
							
								
								
									
										138
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										138
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,138 +0,0 @@ | ||||
| name: Report a bug with the UI, Frontend or Lovelace | ||||
| about: Report an issue related to the Home Assistant frontend. | ||||
| labels: bug | ||||
| title: "" | ||||
| issue_body: true | ||||
| body: | ||||
|   - type: markdown | ||||
|     attributes: | ||||
|       value: | | ||||
|         Make sure you are running the [latest version of Home Assistant][releases] before reporting an issue. | ||||
|  | ||||
|         If you have a feature or enhancement request for the frontend, please [start an discussion][fr] instead of creating an issue. | ||||
|  | ||||
|         **Please not not report issues for custom Lovelace cards.** | ||||
|  | ||||
|         [fr]: https://github.com/home-assistant/frontend/discussions | ||||
|         [releases]: https://github.com/home-assistant/home-assistant/releases | ||||
|   - type: checkboxes | ||||
|     attributes: | ||||
|       label: Checklist | ||||
|       description: Please verify that you've followed these steps | ||||
|       options: | ||||
|         - label: I have updated to the latest available Home Assistant version. | ||||
|           required: true | ||||
|         - label: I have cleared the cache of my browser. | ||||
|           required: true | ||||
|         - label: I have tried a different browser to see if it is related to my browser. | ||||
|           required: true | ||||
|   - type: markdown | ||||
|     attributes: | ||||
|       value: | | ||||
|         ## The problem | ||||
|   - type: textarea | ||||
|     validations: | ||||
|       required: true | ||||
|     attributes: | ||||
|       label: Describe the issue you are experiencing | ||||
|       description: Provide a clear and concise description of what the bug is. | ||||
|   - type: textarea | ||||
|     validations: | ||||
|       required: true | ||||
|     attributes: | ||||
|       label: Describe the behavior you expected | ||||
|       description: Describe what you expected to happen or it should look/behave. | ||||
|   - type: textarea | ||||
|     validations: | ||||
|       required: true | ||||
|     attributes: | ||||
|       label: Steps to reproduce the issue | ||||
|       description: | | ||||
|         Please tell us exactly how to reproduce your issue. | ||||
|         Provide clear and concise step by step instructions and add code snippets if needed. | ||||
|       value: | | ||||
|         1. | ||||
|         2. | ||||
|         3. | ||||
|         ... | ||||
|   - type: markdown | ||||
|     attributes: | ||||
|       value: | | ||||
|         ## Environment | ||||
|   - type: input | ||||
|     validations: | ||||
|       required: true | ||||
|     attributes: | ||||
|       label: What version of Home Assistant Core has the issue? | ||||
|       placeholder: core- | ||||
|       description: > | ||||
|         Can be found in the Configuration panel -> Info. | ||||
|   - type: input | ||||
|     attributes: | ||||
|       label: What was the last working version of Home Assistant Core? | ||||
|       placeholder: core- | ||||
|       description: > | ||||
|         If known, otherwise leave blank. | ||||
|   - type: input | ||||
|     attributes: | ||||
|       label: In which browser are you experiencing the issue with? | ||||
|       placeholder: Google Chrome 88.0.4324.150 | ||||
|       description: > | ||||
|         Provide the full name and don't forget to add the version! | ||||
|   - type: input | ||||
|     attributes: | ||||
|       label: Which operating system are you using to run this browser? | ||||
|       placeholder: macOS Big Sur (1.11) | ||||
|       description: > | ||||
|         Don't forget to add the version! | ||||
|   - type: markdown | ||||
|     attributes: | ||||
|       value: | | ||||
|         # Details | ||||
|  | ||||
|   - type: textarea | ||||
|     attributes: | ||||
|       label: State of relevant entities | ||||
|       description: > | ||||
|         If your issue is about how an entity is shown in the UI, please add the | ||||
|         state and attributes for all situations. You can find this information | ||||
|         at Developer Tools -> States. | ||||
|       value: | | ||||
|         ```yaml | ||||
|         # Paste your state here. | ||||
|  | ||||
|         ``` | ||||
|   - type: textarea | ||||
|     attributes: | ||||
|       label: Problem-relevant frontend configuration | ||||
|       description: > | ||||
|         An example configuration that caused the problem for you, e.g., the YAML | ||||
|         configuration of the used cards. Fill this out even if it seems | ||||
|         unimportant to you. Please be sure to remove personal information like | ||||
|         passwords, private URLs and other credentials. | ||||
|       value: | | ||||
|         ```yaml | ||||
|         # Paste your YAML here. | ||||
|  | ||||
|         ``` | ||||
|   - type: textarea | ||||
|     attributes: | ||||
|       label: Javascript errors shown in your browser console/inspector | ||||
|       description: > | ||||
|         If you come across any Javascript or other error logs, e.g., in your | ||||
|         browser console/inspector please provide them. | ||||
|       value: | | ||||
|         ```txt | ||||
|         # Paste your logs here. | ||||
|  | ||||
|         ``` | ||||
|   - type: markdown | ||||
|     attributes: | ||||
|       value: | | ||||
|         ## Additional information | ||||
|   - type: markdown | ||||
|     attributes: | ||||
|       value: | | ||||
|         If you have any additional information for us, use the field below. | ||||
|         Please note, you can attach screenshots or screen recordings here, | ||||
|         by dragging and dropping files in the field below. | ||||
							
								
								
									
										19
									
								
								.github/workflows/netflify.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								.github/workflows/netflify.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,19 +0,0 @@ | ||||
| name: Netlify | ||||
|  | ||||
| on: | ||||
|   schedule: | ||||
|     - cron: "0 0 * * *" | ||||
|  | ||||
| jobs: | ||||
|   trigger_builds: | ||||
|     name: Trigger netlify build preview | ||||
|     runs-on: "ubuntu-latest" | ||||
|     steps: | ||||
|       - name: Trigger Cast build | ||||
|         run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_CAST_DEV_BUILD_HOOK }} | ||||
|  | ||||
|       - name: Trigger Demo build | ||||
|         run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_DEMO_DEV_BUILD_HOOK }} | ||||
|  | ||||
|       - name: Trigger Gallery build | ||||
|         run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_GALLERY_DEV_BUILD_HOOK }} | ||||
							
								
								
									
										81
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										81
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -1,81 +0,0 @@ | ||||
| name: Release | ||||
|  | ||||
| on: | ||||
|   release: | ||||
|     types: | ||||
|       - published | ||||
|  | ||||
| env: | ||||
|   WHEELS_TAG: 3.7-alpine3.11 | ||||
|   PYTHON_VERSION: 3.7 | ||||
|   NODE_VERSION: 12.1 | ||||
|  | ||||
| jobs: | ||||
|   release: | ||||
|     name: Release | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout the repository | ||||
|         uses: actions/checkout@v2 | ||||
|  | ||||
|       - name: Verify version | ||||
|         uses: home-assistant/actions/helpers/verify-version@master | ||||
|  | ||||
|       - name: Set up Python ${{ env.PYTHON_VERSION }} | ||||
|         uses: actions/setup-python@v2 | ||||
|         with: | ||||
|           python-version: ${{ env.PYTHON_VERSION }} | ||||
|  | ||||
|       - name: Set up Node ${{ env.NODE_VERSION }} | ||||
|         uses: actions/setup-node@v2 | ||||
|         with: | ||||
|           node-version: ${{ env.NODE_VERSION }} | ||||
|  | ||||
|       - name: Build and release package | ||||
|         run: | | ||||
|           python3 -m pip install twine | ||||
|           export TWINE_USERNAME="__token__" | ||||
|           export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}" | ||||
|  | ||||
|           script/release | ||||
|  | ||||
|   wheels-init: | ||||
|     name: Init wheels build | ||||
|     needs: release | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Generate requirements.txt | ||||
|         run: | | ||||
|           # Sleep to give pypi time to populate the new version across mirrors | ||||
|           sleep 240 | ||||
|           version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' ) | ||||
|           echo "home-assistant-frontend==$version" > ./requirements.txt | ||||
|  | ||||
|       - name: Upload requirements.txt | ||||
|         uses: actions/upload-artifact@v2 | ||||
|         with: | ||||
|           name: requirements | ||||
|           path: ./requirements.txt | ||||
|  | ||||
|   build-wheels: | ||||
|     name: Build wheels for ${{ matrix.arch }} | ||||
|     needs: wheels-init | ||||
|     runs-on: ubuntu-latest | ||||
|     strategy: | ||||
|       matrix: | ||||
|         arch: ["aarch64", "armhf", "armv7", "amd64", "i386"] | ||||
|     steps: | ||||
|       - name: Download requirements.txt | ||||
|         uses: actions/download-artifact@v2 | ||||
|         with: | ||||
|           name: requirements | ||||
|  | ||||
|       - name: Build wheels | ||||
|         uses: home-assistant/wheels@master | ||||
|         with: | ||||
|           tag: ${{ env.WHEELS_TAG }} | ||||
|           arch: ${{ matrix.arch }} | ||||
|           wheels-host: ${{ secrets.WHEELS_HOST }} | ||||
|           wheels-key: ${{ secrets.WHEELS_KEY }} | ||||
|           wheels-user: wheels | ||||
|           requirements: "requirements.txt" | ||||
							
								
								
									
										65
									
								
								.github/workflows/translations.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										65
									
								
								.github/workflows/translations.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -1,65 +0,0 @@ | ||||
| name: Translations | ||||
|  | ||||
| on: | ||||
|   schedule: | ||||
|     - cron: "30 0 * * *" | ||||
|   push: | ||||
|     branches: | ||||
|       - dev | ||||
|     paths: | ||||
|       - translations/en.json | ||||
|  | ||||
| env: | ||||
|   NODE_VERSION: 12 | ||||
|  | ||||
| jobs: | ||||
|   upload: | ||||
|     name: Upload | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout the repository | ||||
|         uses: actions/checkout@v2 | ||||
|  | ||||
|       - name: Set up Node ${{ env.NODE_VERSION }} | ||||
|         uses: actions/setup-node@v2 | ||||
|         with: | ||||
|           node-version: ${{ env.NODE_VERSION }} | ||||
|  | ||||
|       - name: Upload Translations | ||||
|         run: | | ||||
|           export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}" | ||||
|  | ||||
|           ./script/translations_upload_base | ||||
|  | ||||
|   download: | ||||
|     name: Download | ||||
|     needs: upload | ||||
|     if: github.event_name == 'schedule' | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout the repository | ||||
|         uses: actions/checkout@v2 | ||||
|  | ||||
|       - name: Set up Node ${{ env.NODE_VERSION }} | ||||
|         uses: actions/setup-node@v2 | ||||
|         with: | ||||
|           node-version: ${{ env.NODE_VERSION }} | ||||
|  | ||||
|       - name: Download Translations | ||||
|         run: | | ||||
|           export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}" | ||||
|  | ||||
|           npm install | ||||
|           ./script/translations_download | ||||
|  | ||||
|       - name: Initialize git | ||||
|         uses: home-assistant/actions/helpers/git-init@master | ||||
|         with: | ||||
|           name: GitHub Action | ||||
|           email: github-action@users.noreply.github.com | ||||
|  | ||||
|       - name: Update translation | ||||
|         run: | | ||||
|           git add translations | ||||
|           git commit -am "Translation update" | ||||
|           git push | ||||
| @@ -14,7 +14,7 @@ This is the repository for the official [Home Assistant](https://home-assistant. | ||||
| - Development: [Instructions](https://developers.home-assistant.io/docs/frontend/development/) | ||||
| - Production build: `script/build_frontend` | ||||
| - Gallery: `cd gallery && script/develop_gallery` | ||||
| - Supervisor: [Instructions](https://developers.home-assistant.io/docs/supervisor/developing) | ||||
| - Hass.io: [Instructions](https://developers.home-assistant.io/docs/en/hassio_hass.html) | ||||
|  | ||||
| ## Frontend development | ||||
|  | ||||
|   | ||||
							
								
								
									
										30
									
								
								azure-pipelines-netlify.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								azure-pipelines-netlify.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| # https://dev.azure.com/home-assistant | ||||
|  | ||||
| trigger: none | ||||
| pr: none | ||||
| schedules: | ||||
|   - cron: "0 0 * * *" | ||||
|     displayName: "build preview" | ||||
|     branches: | ||||
|       include: | ||||
|       - dev | ||||
|     always: true | ||||
| variables: | ||||
|   - group: netlify | ||||
|  | ||||
| jobs: | ||||
|  | ||||
| - job: 'Netlify_preview' | ||||
|   pool: | ||||
|     vmImage: 'ubuntu-latest' | ||||
|   steps: | ||||
|   - script: | | ||||
|       # Cast | ||||
|       curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_CAST} | ||||
|  | ||||
|       # Demo | ||||
|       curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_DEMO} | ||||
|  | ||||
|       # Gallery | ||||
|       curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_GALLERY} | ||||
|     displayName: 'Trigger netlify build preview' | ||||
							
								
								
									
										59
									
								
								azure-pipelines-release.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								azure-pipelines-release.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| # https://dev.azure.com/home-assistant | ||||
|  | ||||
| trigger: | ||||
|   batch: true | ||||
|   tags: | ||||
|     include: | ||||
|       - "*" | ||||
| pr: none | ||||
| variables: | ||||
|   - name: versionWheels | ||||
|     value: '1.10.1-3.7-alpine3.11' | ||||
|   - name: versionNode | ||||
|     value: '12.1' | ||||
|   - group: twine | ||||
| resources: | ||||
|   repositories: | ||||
|     - repository: azure | ||||
|       type: github | ||||
|       name: 'home-assistant/ci-azure' | ||||
|       endpoint: 'home-assistant' | ||||
|  | ||||
|  | ||||
| stages: | ||||
|   - stage: "Validate" | ||||
|     jobs: | ||||
|     - template: templates/azp-job-version.yaml@azure | ||||
|  | ||||
|   - stage: "Build" | ||||
|     jobs: | ||||
|       - job: "ReleasePython" | ||||
|         pool: | ||||
|           vmImage: "ubuntu-latest" | ||||
|         steps: | ||||
|           - task: UsePythonVersion@0 | ||||
|             displayName: "Use Python 3.7" | ||||
|             inputs: | ||||
|               versionSpec: "3.7" | ||||
|           - task: NodeTool@0 | ||||
|             displayName: "Use Node $(versionNode)" | ||||
|             inputs: | ||||
|               versionSpec: "$(versionNode)" | ||||
|           - script: pip install twine wheel | ||||
|             displayName: "Install tools" | ||||
|           - script: | | ||||
|               export TWINE_USERNAME="$(twineUser)" | ||||
|               export TWINE_PASSWORD="$(twinePassword)" | ||||
|  | ||||
|               script/release | ||||
|             displayName: "Build and release package" | ||||
|   - stage: "Wheels" | ||||
|     jobs: | ||||
|       - template: templates/azp-job-wheels.yaml@azure | ||||
|         parameters: | ||||
|           builderVersion: '$(versionWheels)' | ||||
|           wheelsRequirement: 'requirement.txt' | ||||
|           preBuild: | ||||
|           - script: | | ||||
|               sleep 240 | ||||
|               echo "home-assistant-frontend==$(Build.SourceBranchName)" > requirement.txt | ||||
							
								
								
									
										70
									
								
								azure-pipelines-translation.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								azure-pipelines-translation.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| # https://dev.azure.com/home-assistant | ||||
|  | ||||
| trigger: | ||||
|   batch: true | ||||
|   branches: | ||||
|     include: | ||||
|     - dev | ||||
|   paths: | ||||
|     include: | ||||
|     - translations/en.json | ||||
| pr: none | ||||
| schedules: | ||||
|   - cron: "30 0 * * *" | ||||
|     displayName: "frontend translation update" | ||||
|     branches: | ||||
|       include: | ||||
|       - dev | ||||
|     always: true | ||||
| variables: | ||||
| - group: translation | ||||
| resources: | ||||
|   repositories: | ||||
|   - repository: azure | ||||
|     type: github | ||||
|     name: 'home-assistant/ci-azure' | ||||
|     endpoint: 'home-assistant' | ||||
|  | ||||
|  | ||||
| jobs: | ||||
|  | ||||
| - job: 'Upload' | ||||
|   pool: | ||||
|     vmImage: 'ubuntu-latest' | ||||
|   steps: | ||||
|   - task: NodeTool@0 | ||||
|     displayName: 'Use Node 12.x' | ||||
|     inputs: | ||||
|       versionSpec: '12.x' | ||||
|   - script: | | ||||
|       export LOKALISE_TOKEN="$(lokaliseToken)" | ||||
|       export AZURE_BRANCH="$(Build.SourceBranchName)" | ||||
|  | ||||
|       ./script/translations_upload_base | ||||
|     displayName: 'Upload Translation' | ||||
|  | ||||
| - job: 'Download' | ||||
|   dependsOn: | ||||
|   - 'Upload' | ||||
|   condition: or(eq(variables['Build.Reason'], 'Schedule'), eq(variables['Build.Reason'], 'Manual')) | ||||
|   pool: | ||||
|     vmImage: 'ubuntu-latest' | ||||
|   steps: | ||||
|   - task: NodeTool@0 | ||||
|     displayName: 'Use Node 12.x' | ||||
|     inputs: | ||||
|       versionSpec: '12.x' | ||||
|   - template: templates/azp-step-git-init.yaml@azure | ||||
|   - script: | | ||||
|       export LOKALISE_TOKEN="$(lokaliseToken)" | ||||
|       export AZURE_BRANCH="$(Build.SourceBranchName)" | ||||
|  | ||||
|       npm install | ||||
|       ./script/translations_download | ||||
|     displayName: 'Download Translation' | ||||
|   - script: | | ||||
|       git checkout dev | ||||
|       git add translation | ||||
|       git commit -am "[ci skip] Translation update" | ||||
|       git push | ||||
|     displayName: 'Update translation' | ||||
| @@ -36,7 +36,6 @@ const createWebpackConfig = ({ | ||||
|   const ignorePackages = bundle.ignorePackages({ latestBuild }); | ||||
|   return { | ||||
|     mode: isProdBuild ? "production" : "development", | ||||
|     target: ["web", latestBuild ? "es2017" : "es5"], | ||||
|     devtool: isProdBuild | ||||
|       ? "cheap-module-source-map" | ||||
|       : "eval-cheap-module-source-map", | ||||
| @@ -132,6 +131,22 @@ const createWebpackConfig = ({ | ||||
|         } | ||||
|         return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`; | ||||
|       }, | ||||
|       environment: { | ||||
|         // The environment supports arrow functions ('() => { ... }'). | ||||
|         arrowFunction: latestBuild, | ||||
|         // The environment supports BigInt as literal (123n). | ||||
|         bigIntLiteral: false, | ||||
|         // The environment supports const and let for variable declarations. | ||||
|         const: latestBuild, | ||||
|         // The environment supports destructuring ('{ a, b } = obj'). | ||||
|         destructuring: latestBuild, | ||||
|         // The environment supports an async import() function to import EcmaScript modules. | ||||
|         dynamicImport: latestBuild, | ||||
|         // The environment supports 'for of' iteration ('for (const x of array) { ... }'). | ||||
|         forOf: latestBuild, | ||||
|         // The environment supports ECMAScript Module syntax to import ECMAScript modules (import ... from '...'). | ||||
|         module: latestBuild, | ||||
|       }, | ||||
|       chunkFilename: | ||||
|         isProdBuild && !isStatsBuild | ||||
|           ? "chunk.[chunkhash].js" | ||||
|   | ||||
| @@ -48,7 +48,7 @@ class HcCast extends LitElement { | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     if (this.lovelaceConfig === undefined) { | ||||
|       return html`<hass-loading-screen no-toolbar></hass-loading-screen>`; | ||||
|       return html` <hass-loading-screen no-toolbar></hass-loading-screen>> `; | ||||
|     } | ||||
|  | ||||
|     const error = | ||||
|   | ||||
| @@ -98,12 +98,8 @@ class HcLayout extends LitElement { | ||||
|         line-height: 32px; | ||||
|         padding: 24px 16px 16px; | ||||
|         display: block; | ||||
|         margin: 0; | ||||
|       } | ||||
| 	 | ||||
|       .hero { | ||||
|         border-radius: 4px 4px 0 0; | ||||
|       } | ||||
|  | ||||
|       .subtitle { | ||||
|         font-size: 14px; | ||||
|         color: var(--secondary-text-color); | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
|   property, | ||||
|   internalProperty, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { mockHistory } from "../../../../demo/src/stubs/history"; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import "web-animations-js/web-animations-next-lite.min"; | ||||
| import "../../../src/resources/ha-style"; | ||||
| import "../../../src/resources/roboto"; | ||||
| import "../../../src/resources/ha-style"; | ||||
| import "./layout/hc-lovelace"; | ||||
|   | ||||
| @@ -54,8 +54,6 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) => | ||||
|       state: "21", | ||||
|       attributes: { | ||||
|         friendly_name: "Living room temperature", | ||||
|         device_class: "temperature", | ||||
|         unit_of_measurement: "°C", | ||||
|       }, | ||||
|     }, | ||||
|     "sensor.study_temp_rounded": { | ||||
| @@ -63,8 +61,6 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) => | ||||
|       state: "23", | ||||
|       attributes: { | ||||
|         friendly_name: "Study temperature", | ||||
|         device_class: "temperature", | ||||
|         unit_of_measurement: "°C", | ||||
|       }, | ||||
|     }, | ||||
|     "sensor.living_room": { | ||||
| @@ -265,7 +261,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) => | ||||
|       entity_id: "light.kitchen_lights", | ||||
|       state: "off", | ||||
|       attributes: { | ||||
|         friendly_name: "Kitchen Lights", | ||||
|         friendly_name: "Kitchen lights", | ||||
|         supported_features: 1, | ||||
|       }, | ||||
|     }, | ||||
| @@ -488,7 +484,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) => | ||||
|       attributes: { | ||||
|         min_mireds: 111, | ||||
|         max_mireds: 400, | ||||
|         friendly_name: "Garage Lights", | ||||
|         friendly_name: "Garage lights", | ||||
|         supported_features: 55, | ||||
|       }, | ||||
|     }, | ||||
|   | ||||
| @@ -12,7 +12,6 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({ | ||||
|         { | ||||
|           type: "entities", | ||||
|           title: localize("ui.panel.page-demo.config.arsaboo.labels.lights"), | ||||
|           state_color: true, | ||||
|           entities: [ | ||||
|             { | ||||
|               entity: "light.kitchen_lights", | ||||
|   | ||||
| @@ -653,7 +653,7 @@ export const demoEntitiesJimpower: DemoConfig["entities"] = () => | ||||
|       entity_id: "binary_sensor.smoke_sensor_158d0001b8ddc7", | ||||
|       state: "off", | ||||
|       attributes: { | ||||
|         density: 0, | ||||
|         Density: 0, | ||||
|         battery_level: 59, | ||||
|         friendly_name: "Downstairs Smoke Detector", | ||||
|         device_class: "smoke", | ||||
| @@ -663,7 +663,7 @@ export const demoEntitiesJimpower: DemoConfig["entities"] = () => | ||||
|       entity_id: "binary_sensor.smoke_sensor_158d0001b8deba", | ||||
|       state: "off", | ||||
|       attributes: { | ||||
|         density: 0, | ||||
|         Density: 0, | ||||
|         battery_level: 65, | ||||
|         friendly_name: "Upstairs Smoke Detector", | ||||
|         device_class: "smoke", | ||||
|   | ||||
| @@ -3,7 +3,49 @@ import { DemoConfig } from "../types"; | ||||
|  | ||||
| export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({ | ||||
|   name: "Kingia Castle", | ||||
|   resources: [], | ||||
|   resources: [ | ||||
|     // { | ||||
|     //   url: "/local/custom_ui/dark-sky-weather-card.js?v=4", | ||||
|     //   type: "js", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom_ui/mini-media-player-bundle.js?v=0.9.8", | ||||
|     //   type: "module", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom_ui/tracker-card.js?v=0.1.5", | ||||
|     //   type: "js", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom_ui/surveillance-card.js?v=0.0.1", | ||||
|     //   type: "module", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom_ui/mini-graph-card-bundle.js?v=0.1.0", | ||||
|     //   type: "module", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom_ui/slider-entity-row.js?v=d6da75", | ||||
|     //   type: "js", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: | ||||
|     //     "/local/custom_ui/compact-custom-header/compact-custom-header.js?v=0.2.7", | ||||
|     //   type: "js", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom_ui/waze-card.js?v=1.1.1", | ||||
|     //   type: "js", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom_ui/circle-sensor-card.js?v=1.2.0", | ||||
|     //   type: "module", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom_ui/monster-card.js?v=0.2.3", | ||||
|     //   type: "js", | ||||
|     // }, | ||||
|   ], | ||||
|   views: [ | ||||
|     { | ||||
|       cards: [ | ||||
| @@ -561,6 +603,89 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({ | ||||
|         }, | ||||
|         { | ||||
|           cards: [ | ||||
|             // { | ||||
|             //   style: { | ||||
|             //     "background-image": 'url("/assets/jimpower/cardbackK.png")', | ||||
|             //     "background-size": "100% 400px", | ||||
|             //     "box-shadow": "3px 3px rgba(0,0,0,0.4)", | ||||
|             //     "background-repeat": "no-repeat", | ||||
|             //     color: "#999999", | ||||
|             //     "border-radius": "20px", | ||||
|             //     border: "solid 1px rgba(100,100,100,0.3)", | ||||
|             //     "background-color": "rgba(50,50,50,0.3)", | ||||
|             //   }, | ||||
|             //   type: "custom:card-modder", | ||||
|             //   card: { | ||||
|             //     entity_visibility: "sensor.dark_sky_visibility", | ||||
|             //     entity_sun: "sun.sun", | ||||
|             //     entity_daily_summary: | ||||
|             //       "sensor.bom_gc_forecast_detailed_summary_0", | ||||
|             //     entity_temperature: "sensor.bom_temp", | ||||
|             //     entity_forecast_high_temp_3: | ||||
|             //       "sensor.bom_gc_forecast_max_temp_c_3", | ||||
|             //     entity_forecast_high_temp_2: | ||||
|             //       "sensor.bom_gc_forecast_max_temp_c_2", | ||||
|             //     entity_forecast_high_temp_5: | ||||
|             //       "sensor.bom_gc_forecast_max_temp_c_5", | ||||
|             //     entity_forecast_high_temp_4: | ||||
|             //       "sensor.bom_gc_forecast_max_temp_c_4", | ||||
|             //     entity_wind_speed: "sensor.bom_wind_sp", | ||||
|             //     entity_forecast_icon_4: "sensor.dark_sky_icon_4", | ||||
|             //     entity_forecast_icon_5: "sensor.dark_sky_icon_5", | ||||
|             //     entity_forecast_icon_2: "sensor.dark_sky_icon_2", | ||||
|             //     entity_forecast_icon_3: "sensor.dark_sky_icon_3", | ||||
|             //     entity_forecast_icon_1: "sensor.dark_sky_icon_1", | ||||
|             //     entity_forecast_high_temp_1: | ||||
|             //       "sensor.bom_gc_forecast_max_temp_c_1", | ||||
|             //     entity_wind_bearing: "sensor.bom_wind_bear", | ||||
|             //     entity_forecast_low_temp_2: | ||||
|             //       "sensor.bom_gc_forecast_min_temp_c_2", | ||||
|             //     entity_forecast_low_temp_3: | ||||
|             //       "sensor.bom_gc_forecast_min_temp_c_3", | ||||
|             //     entity_pressure: "sensor.bom_pres", | ||||
|             //     entity_forecast_low_temp_1: | ||||
|             //       "sensor.bom_gc_forecast_min_temp_c_1", | ||||
|             //     entity_forecast_low_temp_4: | ||||
|             //       "sensor.bom_gc_forecast_min_temp_c_4", | ||||
|             //     entity_forecast_low_temp_5: | ||||
|             //       "sensor.bom_gc_forecast_min_temp_c_5", | ||||
|             //     entity_humidity: "sensor.bom_humd", | ||||
|             //     type: "custom:dark-sky-weather-card", | ||||
|             //     entity_current_conditions: "sensor.dark_sky_icon", | ||||
|             //   }, | ||||
|             // }, | ||||
|             // { | ||||
|             //   style: { | ||||
|             //     "background-image": 'url("/assets/jimpower/home/waze_5.png")', | ||||
|             //     "background-size": "100% 400px", | ||||
|             //     "box-shadow": "3px 3px rgba(0,0,0,0.4)", | ||||
|             //     "background-repeat": "no-repeat", | ||||
|             //     "border-radius": "20px", | ||||
|             //     border: "solid 1px rgba(100,100,100,0.3)", | ||||
|             //     "background-color": "rgba(50,50,50,0.3)", | ||||
|             //   }, | ||||
|             //   type: "custom:card-modder", | ||||
|             //   card: { | ||||
|             //     entities: [ | ||||
|             //       { | ||||
|             //         name: "James", | ||||
|             //         zone: "zone.home", | ||||
|             //         entity: "sensor.james_to_home", | ||||
|             //       }, | ||||
|             //       { | ||||
|             //         name: "Tina", | ||||
|             //         zone: "zone.home", | ||||
|             //         entity: "sensor.tina_to_home", | ||||
|             //       }, | ||||
|             //       { | ||||
|             //         name: "Work", | ||||
|             //         zone: "zone.powertec", | ||||
|             //         entity: "sensor.commute_to_work", | ||||
|             //       }, | ||||
|             //     ], | ||||
|             //     type: "custom:waze-card", | ||||
|             //   }, | ||||
|             // }, | ||||
|             { | ||||
|               style: { | ||||
|                 "border-radius": "20px", | ||||
| @@ -597,8 +722,46 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({ | ||||
|           ], | ||||
|           type: "vertical-stack", | ||||
|         }, | ||||
|         // { | ||||
|         //   cards: [ | ||||
|         //     { | ||||
|         //       style: { | ||||
|         //         "border-radius": "20px", | ||||
|         //         color: "#999999", | ||||
|         //         "box-shadow": "3px 3px rgba(0,0,0,0.4)", | ||||
|         //         border: "solid 1px rgba(100,100,100,0.3)", | ||||
|         //       }, | ||||
|         //       type: "custom:card-modder", | ||||
|         //       card: { | ||||
|         //         type: "picture-entity", | ||||
|         //         entity: "camera.bom_radar", | ||||
|         //       }, | ||||
|         //     }, | ||||
|         //     // { | ||||
|         //     //   style: { | ||||
|         //     //     "background-image": 'url("/assets/jimpower/cardbackK.png")', | ||||
|         //     //     "background-size": "100% 525px", | ||||
|         //     //     "box-shadow": "3px 3px rgba(0,0,0,0.4)", | ||||
|         //     //     "background-repeat": "no-repeat", | ||||
|         //     //     color: "#999999", | ||||
|         //     //     "border-radius": "20px", | ||||
|         //     //     border: "solid 1px rgba(100,100,100,0.3)", | ||||
|         //     //     "background-color": "rgba(50,50,50,0.3)", | ||||
|         //     //   }, | ||||
|         //     //   type: "custom:card-modder", | ||||
|         //     //   card: { | ||||
|         //     //     title: null, | ||||
|         //     //     type: "custom:tracker-card", | ||||
|         //     //     trackers: [ | ||||
|         //     //       "sensor.custom_card_tracker", | ||||
|         //     //       "sensor.custom_component_tracker", | ||||
|         //     //     ], | ||||
|         //     //   }, | ||||
|         //     // }, | ||||
|         //   ], | ||||
|         //   type: "vertical-stack", | ||||
|         // }, | ||||
|       ], | ||||
|       path: "home", | ||||
|       icon: "mdi:castle", | ||||
|       name: "Home", | ||||
|       background: | ||||
| @@ -718,13 +881,26 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({ | ||||
|               card: { | ||||
|                 image: "/assets/jimpower/security/air_8.jpg", | ||||
|                 elements: [ | ||||
|                   { | ||||
|                     image: | ||||
|                       "https://www.airvisual.com/assets/aqi/ic-face-1-green.svg", | ||||
|                     type: "image", | ||||
|                     style: { | ||||
|                       width: "80px", | ||||
|                       top: "30%", | ||||
|                       left: "12%", | ||||
|                       transform: "none", | ||||
|                       height: "80px", | ||||
|                     }, | ||||
|                     entity: "sensor.us_air_pollution_level_2", | ||||
|                   }, | ||||
|                   { | ||||
|                     style: { | ||||
|                       color: "hsl(120, 41%, 39%)", | ||||
|                       top: "50%", | ||||
|                       "font-weight": 600, | ||||
|                       "font-size": "50px", | ||||
|                       left: "30%", | ||||
|                       "font-size": "20px", | ||||
|                       left: "44%", | ||||
|                     }, | ||||
|                     type: "state-label", | ||||
|                     entity: "sensor.us_air_pollution_level_2", | ||||
| @@ -744,7 +920,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({ | ||||
|                     style: { | ||||
|                       color: "white", | ||||
|                       top: "80%", | ||||
|                       left: "48%", | ||||
|                       left: "52%", | ||||
|                     }, | ||||
|                     type: "state-icon", | ||||
|                     entity: "sensor.us_main_pollutant_2", | ||||
| @@ -1235,7 +1411,6 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({ | ||||
|           type: "vertical-stack", | ||||
|         }, | ||||
|       ], | ||||
|       path: "security", | ||||
|       icon: "hass:shield-home", | ||||
|       name: "Security", | ||||
|       background: | ||||
|   | ||||
| @@ -101,12 +101,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () => | ||||
|     "sensor.zwave_battery_front_door": { | ||||
|       entity_id: "sensor.zwave_battery_front_door", | ||||
|       state: "63", | ||||
|       attributes: { | ||||
|         friendly_name: "Battery", | ||||
|         icon: "mdi:battery-60", | ||||
|         unit_of_measurement: "%", | ||||
|         device_class: "battery", | ||||
|       }, | ||||
|       attributes: { friendly_name: "Battery", icon: "mdi:battery-60" }, | ||||
|     }, | ||||
|     "sensor.oskar_devices": { | ||||
|       entity_id: "sensor.oskar_devices", | ||||
| @@ -169,7 +164,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () => | ||||
|     }, | ||||
|     "input_select.christmas_pattern": { | ||||
|       entity_id: "input_select.christmas_pattern", | ||||
|       state: "Rainbow", | ||||
|       state: "None", | ||||
|       attributes: { | ||||
|         options: [ | ||||
|           "None", | ||||
| @@ -191,7 +186,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () => | ||||
|     }, | ||||
|     "input_select.christmas_palette": { | ||||
|       entity_id: "input_select.christmas_palette", | ||||
|       state: "Party", | ||||
|       state: "None", | ||||
|       attributes: { | ||||
|         options: [ | ||||
|           "None", | ||||
| @@ -462,7 +457,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () => | ||||
|       state: "0.0", | ||||
|       attributes: { | ||||
|         unit_of_measurement: "kB/s", | ||||
|         friendly_name: "Downloading", | ||||
|         friendly_name: "Nedladdning", | ||||
|         icon: "mdi:file-download", | ||||
|       }, | ||||
|     }, | ||||
| @@ -476,7 +471,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () => | ||||
|       state: "0.0", | ||||
|       attributes: { | ||||
|         unit_of_measurement: "kB/s", | ||||
|         friendly_name: "Uploading", | ||||
|         friendly_name: "Uppladdning", | ||||
|         icon: "mdi:file-upload", | ||||
|       }, | ||||
|     }, | ||||
|   | ||||
| @@ -2,7 +2,44 @@ import { DemoConfig } from "../types"; | ||||
|  | ||||
| export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({ | ||||
|   name: "Hem", | ||||
|   resources: [], | ||||
|   resources: [ | ||||
|     // { | ||||
|     //   url: "/local/custom-lovelace/monster-card.js", | ||||
|     //   type: "js", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom-lovelace/mini-media-player-bundle.js?v=0.9.8", | ||||
|     //   type: "module", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom-lovelace/slideshow-card.js?=1.1.0", | ||||
|     //   type: "js", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom-lovelace/fold-entity-row.js?v=3ae2c4", | ||||
|     //   type: "js", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom-lovelace/swipe-card/swipe-card.js?v=2.0.0", | ||||
|     //   type: "module", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom-lovelace/upcoming-media-card/upcoming-media-card.js", | ||||
|     //   type: "js", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom-lovelace/tracker-card.js?v=0.1.5", | ||||
|     //   type: "js", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom-lovelace/card-tools.js?v=6ce5d0", | ||||
|     //   type: "js", | ||||
|     // }, | ||||
|     // { | ||||
|     //   url: "/local/custom-lovelace/krisinfo.js?=0.0.1", | ||||
|     //   type: "js", | ||||
|     // }, | ||||
|   ], | ||||
|   views: [ | ||||
|     { | ||||
|       cards: [ | ||||
| @@ -27,7 +64,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({ | ||||
|                       style: { | ||||
|                         color: "white", | ||||
|                         top: "93%", | ||||
|                         left: "85%", | ||||
|                         left: "90%", | ||||
|                       }, | ||||
|                       type: "state-label", | ||||
|                       entity: "sensor.battery_oskar", | ||||
| @@ -50,7 +87,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({ | ||||
|                     { | ||||
|                       style: { | ||||
|                         color: "white", | ||||
|                         top: "93%", | ||||
|                         top: "92%", | ||||
|                         left: "20%", | ||||
|                       }, | ||||
|                       type: "state-label", | ||||
| @@ -59,8 +96,8 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({ | ||||
|                     { | ||||
|                       style: { | ||||
|                         color: "white", | ||||
|                         top: "93%", | ||||
|                         left: "85%", | ||||
|                         top: "92%", | ||||
|                         left: "90%", | ||||
|                       }, | ||||
|                       type: "state-label", | ||||
|                       entity: "sensor.battery_bella", | ||||
| @@ -68,7 +105,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({ | ||||
|                     { | ||||
|                       style: { | ||||
|                         color: "white", | ||||
|                         top: "93%", | ||||
|                         top: "92%", | ||||
|                         left: "55%", | ||||
|                       }, | ||||
|                       type: "state-label", | ||||
| @@ -94,6 +131,78 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({ | ||||
|           type: "entities", | ||||
|           title: "Lock", | ||||
|         }, | ||||
|         // { | ||||
|         //   filter: { | ||||
|         //     exclude: [ | ||||
|         //       { | ||||
|         //         state: "not_home", | ||||
|         //       }, | ||||
|         //     ], | ||||
|         //     include: [ | ||||
|         //       { | ||||
|         //         entity_id: "device_tracker.annasiphone", | ||||
|         //       }, | ||||
|         //       { | ||||
|         //         entity_id: "device_tracker.iphone_2", | ||||
|         //       }, | ||||
|         //     ], | ||||
|         //   }, | ||||
|         //   type: "custom:monster-card", | ||||
|         //   card: { | ||||
|         //     show_header_toggle: false, | ||||
|         //     type: "entities", | ||||
|         //     title: "G\u00e4ster", | ||||
|         //   }, | ||||
|         //   show_empty: false, | ||||
|         // }, | ||||
|         // { | ||||
|         //   filter: { | ||||
|         //     exclude: [ | ||||
|         //       { | ||||
|         //         state: "Inget", | ||||
|         //       }, | ||||
|         //       { | ||||
|         //         state: "i.u.", | ||||
|         //       }, | ||||
|         //     ], | ||||
|         //     include: [ | ||||
|         //       { | ||||
|         //         entity_id: "sensor.pollen_al", | ||||
|         //       }, | ||||
|         //       { | ||||
|         //         entity_id: "sensor.pollen_alm", | ||||
|         //       }, | ||||
|         //       { | ||||
|         //         entity_id: "sensor.pollen_salg_vide", | ||||
|         //       }, | ||||
|         //       { | ||||
|         //         entity_id: "sensor.pollen_bjork", | ||||
|         //       }, | ||||
|         //       { | ||||
|         //         entity_id: "sensor.pollen_bok", | ||||
|         //       }, | ||||
|         //       { | ||||
|         //         entity_id: "sensor.pollen_ek", | ||||
|         //       }, | ||||
|         //       { | ||||
|         //         entity_id: "sensor.pollen_grabo", | ||||
|         //       }, | ||||
|         //       { | ||||
|         //         entity_id: "sensor.pollen_gras", | ||||
|         //       }, | ||||
|         //       { | ||||
|         //         entity_id: "sensor.pollen_hassel", | ||||
|         //       }, | ||||
|         //     ], | ||||
|         //   }, | ||||
|         //   type: "custom:monster-card", | ||||
|         //   card: { | ||||
|         //     show_header_toggle: false, | ||||
|         //     type: "entities", | ||||
|         //     title: "Pollenniv\u00e5er", | ||||
|         //   }, | ||||
|         //   show_empty: false, | ||||
|         // }, | ||||
|         { | ||||
|           cards: [ | ||||
|             { | ||||
| @@ -117,6 +226,10 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({ | ||||
|           ], | ||||
|           type: "vertical-stack", | ||||
|         }, | ||||
|         // { | ||||
|         //   url: "https://embed.windy.com/embed2.html", | ||||
|         //   type: "iframe", | ||||
|         // }, | ||||
|         { | ||||
|           entities: [ | ||||
|             { | ||||
| @@ -150,7 +263,6 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({ | ||||
|           ], | ||||
|           type: "glance", | ||||
|           show_state: false, | ||||
|           columns: 4, | ||||
|         }, | ||||
|         { | ||||
|           entities: ["sensor.oskar_bluetooth"], | ||||
| @@ -158,6 +270,32 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({ | ||||
|           type: "entities", | ||||
|           title: "Occupancy", | ||||
|         }, | ||||
|         // { | ||||
|         //   filter: { | ||||
|         //     exclude: [ | ||||
|         //       { | ||||
|         //         state: false, | ||||
|         //       }, | ||||
|         //     ], | ||||
|         //     include: [ | ||||
|         //       { | ||||
|         //         entity_id: | ||||
|         //           "binary_sensor.fibaro_system_unknown_type0c02_id1003_sensor_2", | ||||
|         //       }, | ||||
|         //       { | ||||
|         //         entity_id: | ||||
|         //           "binary_sensor.fibaro_system_unknown_type0c02_id1003_sensor_3", | ||||
|         //       }, | ||||
|         //     ], | ||||
|         //   }, | ||||
|         //   type: "custom:monster-card", | ||||
|         //   card: { | ||||
|         //     show_header_toggle: false, | ||||
|         //     type: "entities", | ||||
|         //     title: "Brandvarnare", | ||||
|         //   }, | ||||
|         //   show_empty: false, | ||||
|         // }, | ||||
|         { | ||||
|           type: "weather-forecast", | ||||
|           entity: "weather.smhi_vader", | ||||
| @@ -240,9 +378,41 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({ | ||||
|             "binary_sensor.windows_server", | ||||
|             "binary_sensor.teamspeak", | ||||
|             "binary_sensor.harmony_hub", | ||||
|             // { | ||||
|             //   style: { | ||||
|             //     height: "1px", | ||||
|             //     width: "85%", | ||||
|             //     "margin-left": "auto", | ||||
|             //     background: "#62717b", | ||||
|             //     "margin-right": "auto", | ||||
|             //   }, | ||||
|             //   type: "divider", | ||||
|             // }, | ||||
|             // { | ||||
|             //   items: ["sensor.uptime_router", "sensor.installerad_routeros"], | ||||
|             //   head: { | ||||
|             //     entity: "binary_sensor.router", | ||||
|             //   }, | ||||
|             //   type: "custom:fold-entity-row", | ||||
|             //   group_config: { | ||||
|             //     icon: "mdi:router", | ||||
|             //   }, | ||||
|             // }, | ||||
|             // { | ||||
|             //   items: [ | ||||
|             //     "sensor.uptime_router_server", | ||||
|             //     "sensor.installerad_routeros_server", | ||||
|             //   ], | ||||
|             //   head: { | ||||
|             //     entity: "binary_sensor.router_server", | ||||
|             //   }, | ||||
|             //   type: "custom:fold-entity-row", | ||||
|             //   group_config: { | ||||
|             //     icon: "mdi:router", | ||||
|             //   }, | ||||
|             // }, | ||||
|           ], | ||||
|           show_header_toggle: false, | ||||
|           state_color: true, | ||||
|           type: "entities", | ||||
|           title: "Network", | ||||
|         }, | ||||
| @@ -252,10 +422,29 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({ | ||||
|             "binary_sensor.ubiquiti_switch", | ||||
|             "binary_sensor.ubiquiti_nvr", | ||||
|             "binary_sensor.entre_kamera", | ||||
|             // { | ||||
|             //   items: ["sensor.uptime_ap_1"], | ||||
|             //   head: { | ||||
|             //     entity: "binary_sensor.accesspunkt_1", | ||||
|             //   }, | ||||
|             //   type: "custom:fold-entity-row", | ||||
|             //   group_config: { | ||||
|             //     icon: "router-wireless", | ||||
|             //   }, | ||||
|             // }, | ||||
|             // { | ||||
|             //   items: ["sensor.uptime_ap_2"], | ||||
|             //   head: { | ||||
|             //     entity: "binary_sensor.accesspunkt_2", | ||||
|             //   }, | ||||
|             //   type: "custom:fold-entity-row", | ||||
|             //   group_config: { | ||||
|             //     icon: "router-wireless", | ||||
|             //   }, | ||||
|             // }, | ||||
|             "sensor.total_clients_wireless", | ||||
|           ], | ||||
|           show_header_toggle: false, | ||||
|           state_color: true, | ||||
|           type: "entities", | ||||
|           title: "Ubiquiti", | ||||
|         }, | ||||
|   | ||||
| @@ -215,7 +215,6 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({ | ||||
|           card: { | ||||
|             type: "glance", | ||||
|             show_state: false, | ||||
|             columns: 4, | ||||
|           }, | ||||
|           state_filter: ["on"], | ||||
|         }, | ||||
| @@ -809,6 +808,67 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({ | ||||
|           ], | ||||
|           type: "vertical-stack", | ||||
|         }, | ||||
|         // { | ||||
|         //   cards: [ | ||||
|         //     { | ||||
|         //       entities: [ | ||||
|         //         { | ||||
|         //           hide_when_off: true, | ||||
|         //           toggle: true, | ||||
|         //           type: "custom:slider-entity-row", | ||||
|         //           name: "Bedside", | ||||
|         //           entity: "light.bedside_lamp", | ||||
|         //         }, | ||||
|         //         { | ||||
|         //           hide_when_off: true, | ||||
|         //           toggle: true, | ||||
|         //           type: "custom:slider-entity-row", | ||||
|         //           name: "Bedroom", | ||||
|         //           entity: "light.bedroom_ceiling_light", | ||||
|         //         }, | ||||
|         //         { | ||||
|         //           hide_when_off: true, | ||||
|         //           toggle: true, | ||||
|         //           type: "custom:slider-entity-row", | ||||
|         //           name: "Isa", | ||||
|         //           entity: "light.isa_ceiling_light", | ||||
|         //         }, | ||||
|         //         { | ||||
|         //           hide_when_off: true, | ||||
|         //           toggle: true, | ||||
|         //           type: "custom:slider-entity-row", | ||||
|         //           name: "Upstairs hallway", | ||||
|         //           entity: "light.upstairs_hallway_ceiling_light_level", | ||||
|         //         }, | ||||
|         //         { | ||||
|         //           hide_when_off: true, | ||||
|         //           toggle: true, | ||||
|         //           type: "custom:slider-entity-row", | ||||
|         //           name: "Nightlight", | ||||
|         //           entity: "light.gateway_light_34ce008bfc4b", | ||||
|         //         }, | ||||
|         //         { | ||||
|         //           hide_when_off: true, | ||||
|         //           toggle: true, | ||||
|         //           type: "custom:slider-entity-row", | ||||
|         //           name: "Walk in closet", | ||||
|         //           entity: "light.walk_in_closet_lights", | ||||
|         //         }, | ||||
|         //         { | ||||
|         //           hide_when_off: true, | ||||
|         //           toggle: false, | ||||
|         //           type: "custom:slider-entity-row", | ||||
|         //           name: "Stefan", | ||||
|         //           entity: "light.stefan_lightstrip", | ||||
|         //         }, | ||||
|         //       ], | ||||
|         //       show_header_toggle: false, | ||||
|         //       type: "entities", | ||||
|         //       title: "Upstairs", | ||||
|         //     }, | ||||
|         //   ], | ||||
|         //   type: "vertical-stack", | ||||
|         // }, | ||||
|       ], | ||||
|       path: "lights", | ||||
|       title: "Lights", | ||||
| @@ -858,6 +918,10 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({ | ||||
|                   name: "Dafang", | ||||
|                   icon: "mdi:webcam", | ||||
|                 }, | ||||
|                 { | ||||
|                   name: "IR Hallway", | ||||
|                   entity: "sensor.system_ir_blaster", | ||||
|                 }, | ||||
|                 { | ||||
|                   name: "IR Bedroom", | ||||
|                   entity: "sensor.system_ir_blaster_bedroom", | ||||
| @@ -876,7 +940,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({ | ||||
|                 "sensor.system_ring_chime", | ||||
|               ], | ||||
|               type: "glance", | ||||
|               columns: 4, | ||||
|               columns: 5, | ||||
|               show_state: false, | ||||
|             }, | ||||
|             { | ||||
|   | ||||
| @@ -3,8 +3,8 @@ import { | ||||
|   CSSResult, | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
|   LitElement, | ||||
|   internalProperty, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { CastManager } from "../../../src/cast/cast_manager"; | ||||
|   | ||||
| @@ -3,9 +3,9 @@ import { | ||||
|   css, | ||||
|   CSSResult, | ||||
|   html, | ||||
|   internalProperty, | ||||
|   LitElement, | ||||
|   property, | ||||
|   internalProperty, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { until } from "lit-html/directives/until"; | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| import "../../src/resources/safari-14-attachshadow-patch"; | ||||
| import "@polymer/polymer/lib/elements/dom-if"; | ||||
| import "@polymer/polymer/lib/elements/dom-repeat"; | ||||
| import "../../src/resources/ha-style"; | ||||
| import "../../src/resources/roboto"; | ||||
| import "../../src/resources/safari-14-attachshadow-patch"; | ||||
| import "./ha-demo"; | ||||
|  | ||||
| /* polyfill for paper-dropdown */ | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| // Compat needs to be first import | ||||
| import "../../src/resources/compatibility"; | ||||
| import { isNavigationClick } from "../../src/common/dom/is-navigation-click"; | ||||
| import { navigate } from "../../src/common/navigate"; | ||||
|   | ||||
| @@ -2,10 +2,10 @@ import "@polymer/app-layout/app-toolbar/app-toolbar"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element"; | ||||
| import "../../../src/components/ha-formfield"; | ||||
| import "../../../src/components/ha-switch"; | ||||
| import "../../../src/components/ha-formfield"; | ||||
| import "./demo-card"; | ||||
| import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element"; | ||||
|  | ||||
| class DemoCards extends PolymerElement { | ||||
|   static get template() { | ||||
|   | ||||
| @@ -2,36 +2,37 @@ import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import "../../../src/components/ha-card"; | ||||
| import "../../../src/dialogs/more-info/more-info-content"; | ||||
| import "../../../src/state-summary/state-card-content"; | ||||
| import "../../../src/dialogs/more-info/more-info-content"; | ||||
|  | ||||
| class DemoMoreInfo extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` | ||||
|       <style> | ||||
|         .root { | ||||
|         :host { | ||||
|           display: flex; | ||||
|           align-items: start; | ||||
|         } | ||||
|         #card { | ||||
|           max-width: 400px; | ||||
|           width: 100vw; | ||||
|         } | ||||
|  | ||||
|         ha-card { | ||||
|           width: 352px; | ||||
|           width: 333px; | ||||
|           padding: 20px 24px; | ||||
|         } | ||||
|  | ||||
|         state-card-content { | ||||
|           display: block; | ||||
|           margin-bottom: 16px; | ||||
|         } | ||||
|  | ||||
|         pre { | ||||
|           width: 400px; | ||||
|           margin: 0 16px; | ||||
|           overflow: auto; | ||||
|           color: var(--primary-text-color); | ||||
|         } | ||||
|  | ||||
|         @media only screen and (max-width: 800px) { | ||||
|           .root { | ||||
|           :host { | ||||
|             flex-direction: column; | ||||
|           } | ||||
|           pre { | ||||
| @@ -39,25 +40,21 @@ class DemoMoreInfo extends PolymerElement { | ||||
|           } | ||||
|         } | ||||
|       </style> | ||||
|       <div class="root"> | ||||
|         <div id="card"> | ||||
|           <ha-card> | ||||
|             <state-card-content | ||||
|               state-obj="[[_stateObj]]" | ||||
|               hass="[[hass]]" | ||||
|               in-dialog | ||||
|             ></state-card-content> | ||||
|       <ha-card> | ||||
|         <state-card-content | ||||
|           state-obj="[[_stateObj]]" | ||||
|           hass="[[hass]]" | ||||
|           in-dialog | ||||
|         ></state-card-content> | ||||
|  | ||||
|             <more-info-content | ||||
|               hass="[[hass]]" | ||||
|               state-obj="[[_stateObj]]" | ||||
|             ></more-info-content> | ||||
|           </ha-card> | ||||
|         </div> | ||||
|         <template is="dom-if" if="[[showConfig]]"> | ||||
|           <pre>[[_jsonEntity(_stateObj)]]</pre> | ||||
|         </template> | ||||
|       </div> | ||||
|         <more-info-content | ||||
|           hass="[[hass]]" | ||||
|           state-obj="[[_stateObj]]" | ||||
|         ></more-info-content> | ||||
|       </ha-card> | ||||
|       <template is="dom-if" if="[[showConfig]]"> | ||||
|         <pre>[[_jsonEntity(_stateObj)]]</pre> | ||||
|       </template> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -2,8 +2,6 @@ import "@polymer/app-layout/app-toolbar/app-toolbar"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element"; | ||||
| import "../../../src/components/ha-formfield"; | ||||
| import "../../../src/components/ha-switch"; | ||||
| import "./demo-more-info"; | ||||
|  | ||||
| @@ -11,10 +9,6 @@ class DemoMoreInfos extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` | ||||
|       <style> | ||||
|         #container { | ||||
|           min-height: calc(100vh - 128px); | ||||
|           background: var(--primary-background-color); | ||||
|         } | ||||
|         .cards { | ||||
|           display: flex; | ||||
|           flex-wrap: wrap; | ||||
| @@ -29,31 +23,20 @@ class DemoMoreInfos extends PolymerElement { | ||||
|         .filters { | ||||
|           margin-left: 60px; | ||||
|         } | ||||
|         ha-formfield { | ||||
|           margin-right: 16px; | ||||
|         } | ||||
|       </style> | ||||
|       <app-toolbar> | ||||
|         <div class="filters"> | ||||
|           <ha-formfield label="Show entities"> | ||||
|             <ha-switch checked="[[_showConfig]]" on-change="_showConfigToggled"> | ||||
|             </ha-switch> | ||||
|           </ha-formfield> | ||||
|           <ha-formfield label="Dark theme"> | ||||
|             <ha-switch on-change="_darkThemeToggled"> </ha-switch> | ||||
|           </ha-formfield> | ||||
|           <ha-switch checked="{{_showConfig}}">Show entity</ha-switch> | ||||
|         </div> | ||||
|       </app-toolbar> | ||||
|       <div id="container"> | ||||
|         <div class="cards"> | ||||
|           <template is="dom-repeat" items="[[entities]]"> | ||||
|             <demo-more-info | ||||
|               entity-id="[[item]]" | ||||
|               show-config="[[_showConfig]]" | ||||
|               hass="[[hass]]" | ||||
|             ></demo-more-info> | ||||
|           </template> | ||||
|         </div> | ||||
|       <div class="cards"> | ||||
|         <template is="dom-repeat" items="[[entities]]"> | ||||
|           <demo-more-info | ||||
|             entity-id="[[item]]" | ||||
|             show-config="[[_showConfig]]" | ||||
|             hass="[[hass]]" | ||||
|           ></demo-more-info> | ||||
|         </template> | ||||
|       </div> | ||||
|     `; | ||||
|   } | ||||
| @@ -68,16 +51,6 @@ class DemoMoreInfos extends PolymerElement { | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   _showConfigToggled(ev) { | ||||
|     this._showConfig = ev.target.checked; | ||||
|   } | ||||
|  | ||||
|   _darkThemeToggled(ev) { | ||||
|     applyThemesOnElement(this.$.container, { themes: {} }, "default", { | ||||
|       dark: ev.target.checked, | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| customElements.define("demo-more-infos", DemoMoreInfos); | ||||
|   | ||||
| @@ -7,8 +7,8 @@ export const createMediaPlayerEntities = () => [ | ||||
|     media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)", | ||||
|     media_artist: "Technohead", | ||||
|     // Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media + | ||||
|     // Select Source + Stop + Clear + Play + Shuffle Set | ||||
|     supported_features: 64063, | ||||
|     // Select Source + Stop + Clear + Play + Shuffle Set + Browse Media | ||||
|     supported_features: 195135, | ||||
|     entity_picture: "/images/album_cover_2.jpg", | ||||
|     media_duration: 300, | ||||
|     media_position: 50, | ||||
| @@ -24,8 +24,8 @@ export const createMediaPlayerEntities = () => [ | ||||
|     media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)", | ||||
|     media_artist: "Technohead", | ||||
|     // Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media + | ||||
|     // Select Source + Stop + Clear + Play + Shuffle Set + Browse Media | ||||
|     supported_features: 195135, | ||||
|     // Select Source + Stop + Clear + Play + Shuffle Set | ||||
|     supported_features: 64063, | ||||
|     entity_picture: "/images/album_cover.jpg", | ||||
|     media_duration: 300, | ||||
|     media_position: 0, | ||||
|   | ||||
| @@ -1,72 +0,0 @@ | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
|  | ||||
| export const createPlantEntities = () => [ | ||||
|   getEntity("plant", "lemon_tree", "ok", { | ||||
|     problem: "none", | ||||
|     sensors: { | ||||
|       moisture: "sensor.lemon_tree_moisture", | ||||
|       battery: "sensor.lemon_tree_battery", | ||||
|       temperature: "sensor.lemon_tree_temperature", | ||||
|       conductivity: "sensor.lemon_tree_conductivity", | ||||
|       brightness: "sensor.lemon_tree_brightness", | ||||
|     }, | ||||
|     unit_of_measurement_dict: { | ||||
|       temperature: "°C", | ||||
|       moisture: "%", | ||||
|       brightness: "lx", | ||||
|       battery: "%", | ||||
|       conductivity: "μS/cm", | ||||
|     }, | ||||
|     moisture: 54, | ||||
|     battery: 95, | ||||
|     temperature: 15.6, | ||||
|     conductivity: 1, | ||||
|     brightness: 12, | ||||
|     max_brightness: 20, | ||||
|     friendly_name: "Lemon Tree", | ||||
|   }), | ||||
|   getEntity("plant", "apple_tree", "ok", { | ||||
|     problem: "brightness", | ||||
|     sensors: { | ||||
|       moisture: "sensor.apple_tree_moisture", | ||||
|       battery: "sensor.apple_tree_battery", | ||||
|       temperature: "sensor.apple_tree_temperature", | ||||
|       conductivity: "sensor.apple_tree_conductivity", | ||||
|       brightness: "sensor.apple_tree_brightness", | ||||
|     }, | ||||
|     unit_of_measurement_dict: { | ||||
|       temperature: "°C", | ||||
|       moisture: "%", | ||||
|       brightness: "lx", | ||||
|       battery: "%", | ||||
|       conductivity: "μS/cm", | ||||
|     }, | ||||
|     moisture: 54, | ||||
|     battery: 2, | ||||
|     temperature: 15.6, | ||||
|     conductivity: 1, | ||||
|     brightness: 25, | ||||
|     max_brightness: 20, | ||||
|     friendly_name: "Apple Tree", | ||||
|   }), | ||||
|   getEntity("plant", "sunflowers", "ok", { | ||||
|     problem: "moisture, temperature, conductivity", | ||||
|     sensors: { | ||||
|       moisture: "sensor.sunflowers_moisture", | ||||
|       temperature: "sensor.sunflowers_temperature", | ||||
|       conductivity: "sensor.sunflowers_conductivity", | ||||
|       brightness: "sensor.sunflowers_brightness", | ||||
|     }, | ||||
|     unit_of_measurement_dict: { | ||||
|       temperature: "°C", | ||||
|       moisture: "%", | ||||
|       brightness: "lx", | ||||
|       conductivity: "μS/cm", | ||||
|     }, | ||||
|     moisture: 54, | ||||
|     temperature: 15.6, | ||||
|     conductivity: 1, | ||||
|     brightness: 25, | ||||
|     entity_picture: "/images/sunflowers.jpg", | ||||
|   }), | ||||
| ]; | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| @@ -76,19 +71,28 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-alarm-panel-card") | ||||
| class DemoAlarmPanelEntity extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoAlarmPanelEntity extends PolymerElement { | ||||
|   static get template() { | ||||
|     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"); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     this._setupDemo(); | ||||
|   } | ||||
|  | ||||
|   private async _setupDemo() { | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     await hass.updateTranslations(null, "en"); | ||||
|     hass.addEntities(ENTITIES); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| @@ -58,19 +53,24 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-conditional-card") | ||||
| class DemoConditional extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoConditional extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|     hass.addEntities(ENTITIES); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| @@ -222,19 +217,24 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-entities-card") | ||||
| class DemoEntities extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoEntities extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|     hass.addEntities(ENTITIES); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| @@ -53,7 +48,7 @@ const CONFIGS = [ | ||||
|     config: ` | ||||
| - type: button | ||||
|   entity: light.bed_light | ||||
|   tap_action: | ||||
|   tap_action:  | ||||
|     action: toggle | ||||
|     `, | ||||
|   }, | ||||
| @@ -74,19 +69,24 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-entity-button-card") | ||||
| class DemoButtonEntity extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoButtonEntity extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|     hass.addEntities(ENTITIES); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| @@ -48,7 +43,7 @@ const ENTITIES = [ | ||||
|  | ||||
| const CONFIGS = [ | ||||
|   { | ||||
|     heading: "Unfiltered controller", | ||||
|     heading: "Controller", | ||||
|     config: ` | ||||
| - type: entities | ||||
|   entities: | ||||
| @@ -58,7 +53,7 @@ const CONFIGS = [ | ||||
|     `, | ||||
|   }, | ||||
|   { | ||||
|     heading: "Filtered entities card", | ||||
|     heading: "Basic", | ||||
|     config: ` | ||||
| - type: entity-filter | ||||
|   entities: | ||||
| @@ -74,27 +69,7 @@ const CONFIGS = [ | ||||
|     `, | ||||
|   }, | ||||
|   { | ||||
|     heading: 'With "entities" card config', | ||||
|     config: ` | ||||
| - type: entity-filter | ||||
|   entities: | ||||
|     - device_tracker.demo_anne_therese | ||||
|     - device_tracker.demo_home_boy | ||||
|     - device_tracker.demo_paulus | ||||
|     - light.bed_light | ||||
|     - light.ceiling_lights | ||||
|     - light.kitchen_lights | ||||
|   state_filter: | ||||
|     - "on" | ||||
|     - not_home | ||||
|   card: | ||||
|     type: entities | ||||
|     title: Custom Title | ||||
|     show_header_toggle: false | ||||
|     `, | ||||
|   }, | ||||
|   { | ||||
|     heading: 'With "glance" card config', | ||||
|     heading: "With card config", | ||||
|     config: ` | ||||
| - type: entity-filter | ||||
|   entities: | ||||
| @@ -109,27 +84,31 @@ const CONFIGS = [ | ||||
|     - not_home | ||||
|   card: | ||||
|     type: glance | ||||
|     show_state: true | ||||
|     title: Custom Title | ||||
|     show_state: false | ||||
|     `, | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-entity-filter-card") | ||||
| class DemoEntityFilter extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoFilter extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|     hass.addEntities(ENTITIES); | ||||
|   } | ||||
| } | ||||
|  | ||||
| customElements.define("demo-hui-entity-filter-card", DemoEntityFilter); | ||||
| customElements.define("demo-hui-entity-filter-card", DemoFilter); | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| @@ -112,19 +107,24 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-gauge-card") | ||||
| class DemoGaugeEntity extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoGaugeEntity extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|     hass.addEntities(ENTITIES); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| @@ -82,8 +77,7 @@ const CONFIGS = [ | ||||
|     heading: "With title", | ||||
|     config: ` | ||||
| - type: glance | ||||
|   title: Custom title | ||||
|   columns: 4 | ||||
|   title: This is glance | ||||
|   entities: | ||||
|     - device_tracker.demo_paulus | ||||
|     - media_player.living_room | ||||
| @@ -110,10 +104,9 @@ const CONFIGS = [ | ||||
|     `, | ||||
|   }, | ||||
|   { | ||||
|     heading: "No entity names", | ||||
|     heading: "No name", | ||||
|     config: ` | ||||
| - type: glance | ||||
|   columns: 4 | ||||
|   show_name: false | ||||
|   entities: | ||||
|     - device_tracker.demo_paulus | ||||
| @@ -126,10 +119,9 @@ const CONFIGS = [ | ||||
|     `, | ||||
|   }, | ||||
|   { | ||||
|     heading: "No state labels", | ||||
|     heading: "No state", | ||||
|     config: ` | ||||
| - type: glance | ||||
|   columns: 4 | ||||
|   show_state: false | ||||
|   entities: | ||||
|     - device_tracker.demo_paulus | ||||
| @@ -142,10 +134,9 @@ const CONFIGS = [ | ||||
|     `, | ||||
|   }, | ||||
|   { | ||||
|     heading: "No names and no state labels", | ||||
|     heading: "No name and no state", | ||||
|     config: ` | ||||
| - type: glance | ||||
|   columns: 4 | ||||
|   show_name: false | ||||
|   show_state: false | ||||
|   entities: | ||||
| @@ -159,24 +150,47 @@ const CONFIGS = [ | ||||
|     `, | ||||
|   }, | ||||
|   { | ||||
|     heading: "Custom name + custom icon", | ||||
|     heading: "Custom name, custom icon", | ||||
|     config: ` | ||||
| - type: glance | ||||
|   columns: 4 | ||||
|   entities: | ||||
|     - entity: device_tracker.demo_paulus | ||||
|       name: ¯\\_(ツ)_/¯ | ||||
|       icon: mdi:home-assistant | ||||
|     - entity: media_player.living_room | ||||
|       name: ¯\\_(ツ)_/¯ | ||||
|       icon: mdi:home-assistant | ||||
|     - media_player.living_room | ||||
|     - sun.sun | ||||
|     - cover.kitchen_window | ||||
|     - entity: light.kitchen_lights | ||||
|       icon: mdi:alarm-light | ||||
|     - lock.kitchen_door | ||||
|     - light.ceiling_lights | ||||
|     `, | ||||
|   }, | ||||
|   { | ||||
|     heading: "Custom tap action", | ||||
|     config: ` | ||||
| - type: glance | ||||
|   entities: | ||||
|     - entity: lock.kitchen_door | ||||
|       tap_action: | ||||
|         type: toggle | ||||
|     - entity: light.ceiling_lights | ||||
|       tap_action: | ||||
|         action: call-service | ||||
|         service: light.turn_on | ||||
|         service_data: | ||||
|           entity_id: light.ceiling_lights | ||||
|     - device_tracker.demo_paulus | ||||
|     - media_player.living_room | ||||
|     - sun.sun | ||||
|     - cover.kitchen_window | ||||
|     - light.kitchen_lights | ||||
|     `, | ||||
|   }, | ||||
|   { | ||||
|     heading: "Selectively hidden name", | ||||
|     config: ` | ||||
| - type: glance | ||||
|   columns: 4 | ||||
|   entities: | ||||
|     - device_tracker.demo_paulus | ||||
|     - entity: media_player.living_room | ||||
| @@ -185,51 +199,45 @@ const CONFIGS = [ | ||||
|     - entity: cover.kitchen_window | ||||
|       name: | ||||
|     - light.kitchen_lights | ||||
|     - entity: lock.kitchen_door | ||||
|       name:  | ||||
|     - light.ceiling_lights | ||||
|     `, | ||||
|   }, | ||||
|   { | ||||
|     heading: "Custom tap action", | ||||
|     heading: "Primary theme", | ||||
|     config: ` | ||||
| - type: glance | ||||
|   columns: 4   | ||||
|   theming: primary | ||||
|   entities: | ||||
|     - entity: lock.kitchen_door | ||||
|       name: Custom | ||||
|       tap_action: | ||||
|         type: toggle | ||||
|     - entity: light.ceiling_lights | ||||
|       name: Custom | ||||
|       tap_action: | ||||
|         action: call-service | ||||
|         service: light.turn_on | ||||
|         service_data: | ||||
|           entity_id: light.ceiling_lights | ||||
|     - entity: sun.sun | ||||
|       name: Regular | ||||
|     - entity: light.kitchen_lights | ||||
|       name: Regular | ||||
|     - device_tracker.demo_paulus | ||||
|     - media_player.living_room | ||||
|     - sun.sun | ||||
|     - cover.kitchen_window | ||||
|     - light.kitchen_lights | ||||
|     - lock.kitchen_door | ||||
|     - light.ceiling_lights | ||||
|     `, | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-glance-card") | ||||
| class DemoGlanceEntity extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoPicEntity extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|     hass.addEntities(ENTITIES); | ||||
|   } | ||||
| } | ||||
|  | ||||
| customElements.define("demo-hui-glance-card", DemoGlanceEntity); | ||||
| customElements.define("demo-hui-glance-card", DemoPicEntity); | ||||
|   | ||||
| @@ -1,4 +1,6 @@ | ||||
| import { customElement, html, LitElement, TemplateResult } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import "../components/demo-cards"; | ||||
|  | ||||
| const CONFIGS = [ | ||||
| @@ -35,10 +37,18 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-iframe-card") | ||||
| class DemoIframe extends LitElement { | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoIframe extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| @@ -68,19 +63,24 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-light-card") | ||||
| class DemoLightEntity extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoLightEntity extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|     hass.addEntities(ENTITIES); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| @@ -166,19 +161,24 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-map-card") | ||||
| class DemoMap extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoMap extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|     hass.addEntities(ENTITIES); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { mockTemplate } from "../../../demo/src/stubs/template"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| @@ -259,19 +254,23 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-markdown-card") | ||||
| class DemoMarkdown extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoMarkdown extends PolymerElement { | ||||
|   static get template() { | ||||
|     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"); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     mockTemplate(hass); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| import { createMediaPlayerEntities } from "../data/media_players"; | ||||
| @@ -151,33 +146,28 @@ const CONFIGS = [ | ||||
|     entity: media_player.receiver_off | ||||
|     `, | ||||
|   }, | ||||
|   { | ||||
|     heading: "Grid Full Size", | ||||
|     config: ` | ||||
|   - type: grid | ||||
|     columns: 1 | ||||
|     cards: | ||||
|     - type: media-control | ||||
|       entity: media_player.music_paused | ||||
|     `, | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-media-control-card") | ||||
| class DemoHuiMediaControlCard extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoHuiMediControlCard extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|     hass.addEntities(createMediaPlayerEntities()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| customElements.define("demo-hui-media-control-card", DemoHuiMediaControlCard); | ||||
| customElements.define("demo-hui-media-control-card", DemoHuiMediControlCard); | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| import { createMediaPlayerEntities } from "../data/media_players"; | ||||
| @@ -31,9 +26,9 @@ const CONFIGS = [ | ||||
|     - entity: media_player.android_cast | ||||
|       name: Screen casting | ||||
|     - entity: media_player.image_display | ||||
|       name: Digital Picture Frame | ||||
|       name: Digital Picture Frame   | ||||
|     - entity: media_player.sonos_idle | ||||
|       name: Sonos Idle | ||||
|       name: Sonos Idle   | ||||
|     - entity: media_player.idle_browse_media | ||||
|       name: Idle waiting for Browse Media | ||||
|     - entity: media_player.theater_off | ||||
| @@ -43,7 +38,7 @@ const CONFIGS = [ | ||||
|     - entity: media_player.theater_off_static | ||||
|       name: Player Off (cannot be switched on) | ||||
|     - entity: media_player.theater_on_static | ||||
|       name: Player On (cannot be switched off) | ||||
|       name: Player On (cannot be switched off)   | ||||
|     - entity: media_player.idle | ||||
|       name: Player Idle | ||||
|     - entity: media_player.playing | ||||
| @@ -60,21 +55,26 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-media-player-row") | ||||
| class DemoHuiMediaPlayerRow extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoHuiMediaPlayerRows extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|     hass.addEntities(createMediaPlayerEntities()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| customElements.define("demo-hui-media-player-row", DemoHuiMediaPlayerRow); | ||||
| customElements.define("demo-hui-media-player-rows", DemoHuiMediaPlayerRows); | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| @@ -130,21 +125,26 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-picture-elements-card") | ||||
| class DemoPictureElements extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoPicElements extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|     hass.addEntities(ENTITIES); | ||||
|   } | ||||
| } | ||||
|  | ||||
| customElements.define("demo-hui-picture-elements-card", DemoPictureElements); | ||||
| customElements.define("demo-hui-picture-elements-card", DemoPicElements); | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| @@ -85,21 +80,26 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-picture-entity-card") | ||||
| class DemoPictureEntity extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoPicEntity extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|     hass.addEntities(ENTITIES); | ||||
|   } | ||||
| } | ||||
|  | ||||
| customElements.define("demo-hui-picture-entity-card", DemoPictureEntity); | ||||
| customElements.define("demo-hui-picture-entity-card", DemoPicEntity); | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| @@ -126,21 +121,26 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-picture-glance-card") | ||||
| class DemoPictureGlance extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoPicGlance extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|     hass.addEntities(ENTITIES); | ||||
|   } | ||||
| } | ||||
|  | ||||
| customElements.define("demo-hui-picture-glance-card", DemoPictureGlance); | ||||
| customElements.define("demo-hui-picture-glance-card", DemoPicGlance); | ||||
|   | ||||
| @@ -1,55 +0,0 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| import { createPlantEntities } from "../data/plants"; | ||||
|  | ||||
| const CONFIGS = [ | ||||
|   { | ||||
|     heading: "Basic example", | ||||
|     config: ` | ||||
| - type: plant-status | ||||
|   entity: plant.lemon_tree | ||||
|     `, | ||||
|   }, | ||||
|   { | ||||
|     heading: "Problem (too bright) + low battery", | ||||
|     config: ` | ||||
| - type: plant-status | ||||
|   entity: plant.apple_tree | ||||
|     `, | ||||
|   }, | ||||
|   { | ||||
|     heading: "With picture + multiple problems", | ||||
|     config: ` | ||||
| - type: plant-status | ||||
|   entity: plant.sunflowers | ||||
|   name: Sunflowers Name Overwrite | ||||
|     `, | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-plant-card") | ||||
| export class DemoPlantEntity 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(createPlantEntities()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| customElements.define("demo-hui-plant-card", DemoPlantEntity); | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
|  | ||||
| @@ -25,19 +20,24 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-shopping-list-card") | ||||
| class DemoShoppingListEntity extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoShoppingListEntity extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|  | ||||
|     hass.mockAPI("shopping_list", () => [ | ||||
|       { name: "list", id: 1, complete: false }, | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { mockHistory } from "../../../demo/src/stubs/history"; | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| @@ -137,19 +132,24 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-stack-card") | ||||
| class DemoStack extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoStack extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|     hass.addEntities(ENTITIES); | ||||
|     mockHistory(hass); | ||||
|   } | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-cards"; | ||||
| @@ -79,19 +74,24 @@ const CONFIGS = [ | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-hui-thermostat-card") | ||||
| class DemoThermostatEntity extends LitElement { | ||||
|   @query("#demos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; | ||||
| class DemoThermostatEntity extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _configs: { | ||||
|         type: Object, | ||||
|         value: CONFIGS, | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     const hass = provideHass(this.$.demos); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|     hass.updateTranslations("lovelace", "en"); | ||||
|     hass.addEntities(ENTITIES); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,29 +1,12 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   property, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; | ||||
| /* eslint-plugin-disable lit */ | ||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; | ||||
| import "../../../src/components/ha-card"; | ||||
| import { | ||||
|   SUPPORT_BRIGHTNESS, | ||||
|   SUPPORT_COLOR, | ||||
|   SUPPORT_COLOR_TEMP, | ||||
|   SUPPORT_EFFECT, | ||||
|   SUPPORT_FLASH, | ||||
|   SUPPORT_TRANSITION, | ||||
|   SUPPORT_WHITE_VALUE, | ||||
| } from "../../../src/data/light"; | ||||
| import "../../../src/dialogs/more-info/more-info-content"; | ||||
| import { SUPPORT_BRIGHTNESS } from "../../../src/data/light"; | ||||
| import { getEntity } from "../../../src/fake_data/entity"; | ||||
| import { | ||||
|   MockHomeAssistant, | ||||
|   provideHass, | ||||
| } from "../../../src/fake_data/provide_hass"; | ||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; | ||||
| import "../components/demo-more-infos"; | ||||
| import "../../../src/dialogs/more-info/more-info-content"; | ||||
|  | ||||
| const ENTITIES = [ | ||||
|   getEntity("light", "bed_light", "on", { | ||||
| @@ -31,52 +14,38 @@ const ENTITIES = [ | ||||
|   }), | ||||
|   getEntity("light", "kitchen_light", "on", { | ||||
|     friendly_name: "Brightness Light", | ||||
|     brightness: 200, | ||||
|     brightness: 80, | ||||
|     supported_features: SUPPORT_BRIGHTNESS, | ||||
|   }), | ||||
|   getEntity("light", "color_temperature_light", "on", { | ||||
|     friendly_name: "White Color Temperature Light", | ||||
|     brightness: 128, | ||||
|     color_temp: 75, | ||||
|     min_mireds: 30, | ||||
|     max_mireds: 150, | ||||
|     supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP, | ||||
|   }), | ||||
|   getEntity("light", "color_effectslight", "on", { | ||||
|     friendly_name: "Color Effets Light", | ||||
|     brightness: 255, | ||||
|     hs_color: [30, 100], | ||||
|     white_value: 36, | ||||
|     supported_features: | ||||
|       SUPPORT_BRIGHTNESS + | ||||
|       SUPPORT_EFFECT + | ||||
|       SUPPORT_FLASH + | ||||
|       SUPPORT_COLOR + | ||||
|       SUPPORT_TRANSITION + | ||||
|       SUPPORT_WHITE_VALUE, | ||||
|     effect_list: ["random", "colorloop"], | ||||
|   }), | ||||
| ]; | ||||
|  | ||||
| @customElement("demo-more-info-light") | ||||
| class DemoMoreInfoLight extends LitElement { | ||||
|   @property() public hass!: MockHomeAssistant; | ||||
|  | ||||
|   @query("demo-more-infos") private _demoRoot!: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
| class DemoMoreInfoLight extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` | ||||
|       <demo-more-infos | ||||
|         .hass=${this.hass} | ||||
|         .entities=${ENTITIES.map((ent) => ent.entityId)} | ||||
|         hass="[[hass]]" | ||||
|         entities="[[_entities]]" | ||||
|       ></demo-more-infos> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProperties: PropertyValues) { | ||||
|     super.firstUpdated(changedProperties); | ||||
|     const hass = provideHass(this._demoRoot); | ||||
|     hass.updateTranslations(null, "en"); | ||||
|   static get properties() { | ||||
|     return { | ||||
|       _entities: { | ||||
|         type: Array, | ||||
|         value: ENTITIES.map((ent) => ent.entityId), | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public ready() { | ||||
|     super.ready(); | ||||
|     this._setupDemo(); | ||||
|   } | ||||
|  | ||||
|   private async _setupDemo() { | ||||
|     const hass = provideHass(this); | ||||
|     await hass.updateTranslations(null, "en"); | ||||
|     hass.addEntities(ENTITIES); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,9 @@ | ||||
| import "@material/mwc-button"; | ||||
| import { customElement, html, LitElement, TemplateResult } from "lit-element"; | ||||
| import { html, LitElement, TemplateResult } from "lit-element"; | ||||
| import "../../../src/components/ha-card"; | ||||
| import { ActionHandlerEvent } from "../../../src/data/lovelace"; | ||||
| import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive"; | ||||
|  | ||||
| @customElement("demo-util-long-press") | ||||
| export class DemoUtilLongPress extends LitElement { | ||||
|   protected render(): TemplateResult { | ||||
|     return html` | ||||
| @@ -21,7 +20,7 @@ export class DemoUtilLongPress extends LitElement { | ||||
|  | ||||
|             <textarea></textarea> | ||||
|  | ||||
|             <div>Try pressing and scrolling too!</div> | ||||
|             <div>(try pressing and scrolling too!)</div> | ||||
|           </ha-card> | ||||
|         ` | ||||
|       )} | ||||
| @@ -63,3 +62,5 @@ export class DemoUtilLongPress extends LitElement { | ||||
|     `; | ||||
|   } | ||||
| } | ||||
|  | ||||
| customElements.define("demo-util-long-press", DemoUtilLongPress); | ||||
|   | ||||
| @@ -14,6 +14,8 @@ import "../../src/styles/polymer-ha-style"; | ||||
| // eslint-disable-next-line import/extensions | ||||
| import { DEMOS } from "../build/import-demos"; | ||||
|  | ||||
| const fixPath = (path) => path.substr(2, path.length - 5); | ||||
|  | ||||
| class HaGallery extends PolymerElement { | ||||
|   static get template() { | ||||
|     return html` | ||||
|   | ||||
| @@ -11,18 +11,17 @@ import { | ||||
|   PropertyValues, | ||||
| } from "lit-element"; | ||||
| import { html, TemplateResult } from "lit-html"; | ||||
| import memoizeOne from "memoize-one"; | ||||
| import { atLeastVersion } from "../../../src/common/config/version"; | ||||
| import { fireEvent } from "../../../src/common/dom/fire_event"; | ||||
| import "../../../src/common/search/search-input"; | ||||
| import "../../../src/components/ha-button-menu"; | ||||
| import "../../../src/components/ha-svg-icon"; | ||||
| import { | ||||
|   fetchHassioAddonsInfo, | ||||
|   HassioAddonInfo, | ||||
|   HassioAddonRepository, | ||||
|   reloadHassioAddons, | ||||
| } from "../../../src/data/hassio/addon"; | ||||
| import { Supervisor } from "../../../src/data/supervisor/supervisor"; | ||||
| import { extractApiErrorMessage } from "../../../src/data/hassio/common"; | ||||
| import "../../../src/layouts/hass-loading-screen"; | ||||
| import "../../../src/layouts/hass-tabs-subpage"; | ||||
| import { HomeAssistant, Route } from "../../../src/types"; | ||||
| @@ -50,27 +49,46 @@ const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => { | ||||
| class HassioAddonStore extends LitElement { | ||||
|   @property({ attribute: false }) public hass!: HomeAssistant; | ||||
|  | ||||
|   @property({ attribute: false }) public supervisor!: Supervisor; | ||||
|  | ||||
|   @property({ type: Boolean }) public narrow!: boolean; | ||||
|  | ||||
|   @property({ attribute: false }) public route!: Route; | ||||
|  | ||||
|   @property({ attribute: false }) private _addons?: HassioAddonInfo[]; | ||||
|  | ||||
|   @property({ attribute: false }) private _repos?: HassioAddonRepository[]; | ||||
|  | ||||
|   @internalProperty() private _filter?: string; | ||||
|  | ||||
|   public async refreshData() { | ||||
|     this._repos = undefined; | ||||
|     this._addons = undefined; | ||||
|     this._filter = undefined; | ||||
|     await reloadHassioAddons(this.hass); | ||||
|     await this._loadData(); | ||||
|   } | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     let repos: TemplateResult[] = []; | ||||
|     const repos: TemplateResult[] = []; | ||||
|  | ||||
|     if (this.supervisor.addon.repositories) { | ||||
|       repos = this.addonRepositories( | ||||
|         this.supervisor.addon.repositories, | ||||
|         this.supervisor.addon.addons | ||||
|       ); | ||||
|     if (this._repos) { | ||||
|       for (const repo of this._repos) { | ||||
|         const addons = this._addons!.filter( | ||||
|           (addon) => addon.repository === repo.slug | ||||
|         ); | ||||
|  | ||||
|         if (addons.length === 0) { | ||||
|           continue; | ||||
|         } | ||||
|  | ||||
|         repos.push(html` | ||||
|           <hassio-addon-repository | ||||
|             .hass=${this.hass} | ||||
|             .repo=${repo} | ||||
|             .addons=${addons} | ||||
|             .filter=${this._filter!} | ||||
|           ></hassio-addon-repository> | ||||
|         `); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return html` | ||||
| @@ -139,27 +157,6 @@ class HassioAddonStore extends LitElement { | ||||
|     this._loadData(); | ||||
|   } | ||||
|  | ||||
|   private addonRepositories = memoizeOne( | ||||
|     (repositories: HassioAddonRepository[], addons: HassioAddonInfo[]) => { | ||||
|       return repositories.sort(sortRepos).map((repo) => { | ||||
|         const filteredAddons = addons.filter( | ||||
|           (addon) => addon.repository === repo.slug | ||||
|         ); | ||||
|  | ||||
|         return filteredAddons.length !== 0 | ||||
|           ? html` | ||||
|               <hassio-addon-repository | ||||
|                 .hass=${this.hass} | ||||
|                 .repo=${repo} | ||||
|                 .addons=${filteredAddons} | ||||
|                 .filter=${this._filter!} | ||||
|               ></hassio-addon-repository> | ||||
|             ` | ||||
|           : html``; | ||||
|       }); | ||||
|     } | ||||
|   ); | ||||
|  | ||||
|   private _handleAction(ev: CustomEvent<ActionDetail>) { | ||||
|     switch (ev.detail.index) { | ||||
|       case 0: | ||||
| @@ -182,7 +179,7 @@ class HassioAddonStore extends LitElement { | ||||
|  | ||||
|   private async _manageRepositories() { | ||||
|     showRepositoriesDialog(this, { | ||||
|       repos: this.supervisor.addon.repositories, | ||||
|       repos: this._repos!, | ||||
|       loadData: () => this._loadData(), | ||||
|     }); | ||||
|   } | ||||
| @@ -192,8 +189,14 @@ class HassioAddonStore extends LitElement { | ||||
|   } | ||||
|  | ||||
|   private async _loadData() { | ||||
|     fireEvent(this, "supervisor-store-refresh", { store: "addon" }); | ||||
|     fireEvent(this, "supervisor-store-refresh", { store: "supervisor" }); | ||||
|     try { | ||||
|       const addonsInfo = await fetchHassioAddonsInfo(this.hass); | ||||
|       this._repos = addonsInfo.repositories; | ||||
|       this._repos.sort(sortRepos); | ||||
|       this._addons = addonsInfo.addons; | ||||
|     } catch (err) { | ||||
|       alert(extractApiErrorMessage(err)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private async _filterChanged(e) { | ||||
|   | ||||
| @@ -7,14 +7,13 @@ import { | ||||
|   CSSResult, | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
|   LitElement, | ||||
|   property, | ||||
|   internalProperty, | ||||
|   PropertyValues, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import "web-animations-js/web-animations-next-lite.min"; | ||||
| import "../../../../src/components/buttons/ha-progress-button"; | ||||
| import "../../../../src/components/ha-card"; | ||||
| import { | ||||
|   HassioAddonDetails, | ||||
| @@ -29,6 +28,7 @@ import { haStyle } from "../../../../src/resources/styles"; | ||||
| import { HomeAssistant } from "../../../../src/types"; | ||||
| import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart"; | ||||
| import { hassioStyle } from "../../resources/hassio-style"; | ||||
| import "../../../../src/components/buttons/ha-progress-button"; | ||||
|  | ||||
| @customElement("hassio-addon-audio") | ||||
| class HassioAddonAudio extends LitElement { | ||||
|   | ||||
| @@ -7,11 +7,11 @@ import { | ||||
|   property, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import "../../../../src/components/ha-circular-progress"; | ||||
| import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; | ||||
| import { haStyle } from "../../../../src/resources/styles"; | ||||
| import { HomeAssistant } from "../../../../src/types"; | ||||
| import { hassioStyle } from "../../resources/hassio-style"; | ||||
| import "../../../../src/components/ha-circular-progress"; | ||||
| import "./hassio-addon-audio"; | ||||
| import "./hassio-addon-config"; | ||||
| import "./hassio-addon-network"; | ||||
| @@ -26,41 +26,28 @@ class HassioAddonConfigDashboard extends LitElement { | ||||
|     if (!this.addon) { | ||||
|       return html`<ha-circular-progress active></ha-circular-progress>`; | ||||
|     } | ||||
|     const hasOptions = | ||||
|       this.addon.options && Object.keys(this.addon.options).length; | ||||
|     const hasSchema = | ||||
|       hasOptions && this.addon.schema && Object.keys(this.addon.schema).length; | ||||
|  | ||||
|     return html` | ||||
|       <div class="content"> | ||||
|         ${hasOptions || hasSchema || this.addon.network || this.addon.audio | ||||
|         <hassio-addon-config | ||||
|           .hass=${this.hass} | ||||
|           .addon=${this.addon} | ||||
|         ></hassio-addon-config> | ||||
|         ${this.addon.network | ||||
|           ? html` | ||||
|               ${hasOptions || hasSchema | ||||
|                 ? html` | ||||
|                     <hassio-addon-config | ||||
|                       .hass=${this.hass} | ||||
|                       .addon=${this.addon} | ||||
|                     ></hassio-addon-config> | ||||
|                   ` | ||||
|                 : ""} | ||||
|               ${this.addon.network | ||||
|                 ? html` | ||||
|                     <hassio-addon-network | ||||
|                       .hass=${this.hass} | ||||
|                       .addon=${this.addon} | ||||
|                     ></hassio-addon-network> | ||||
|                   ` | ||||
|                 : ""} | ||||
|               ${this.addon.audio | ||||
|                 ? html` | ||||
|                     <hassio-addon-audio | ||||
|                       .hass=${this.hass} | ||||
|                       .addon=${this.addon} | ||||
|                     ></hassio-addon-audio> | ||||
|                   ` | ||||
|                 : ""} | ||||
|               <hassio-addon-network | ||||
|                 .hass=${this.hass} | ||||
|                 .addon=${this.addon} | ||||
|               ></hassio-addon-network> | ||||
|             ` | ||||
|           : "This add-on does not expose configuration for you to mess with.... 👋"} | ||||
|           : ""} | ||||
|         ${this.addon.audio | ||||
|           ? html` | ||||
|               <hassio-addon-audio | ||||
|                 .hass=${this.hass} | ||||
|                 .addon=${this.addon} | ||||
|               ></hassio-addon-audio> | ||||
|             ` | ||||
|           : ""} | ||||
|       </div> | ||||
|     `; | ||||
|   } | ||||
|   | ||||
| @@ -1,7 +1,4 @@ | ||||
| import "@material/mwc-button"; | ||||
| import { ActionDetail } from "@material/mwc-list"; | ||||
| import "@material/mwc-list/mwc-list-item"; | ||||
| import { mdiDotsVertical } from "@mdi/js"; | ||||
| import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea"; | ||||
| import { | ||||
|   css, | ||||
| @@ -17,9 +14,7 @@ import { | ||||
| } from "lit-element"; | ||||
| import { fireEvent } from "../../../../src/common/dom/fire_event"; | ||||
| import "../../../../src/components/buttons/ha-progress-button"; | ||||
| import "../../../../src/components/ha-button-menu"; | ||||
| import "../../../../src/components/ha-card"; | ||||
| import "../../../../src/components/ha-form/ha-form"; | ||||
| import "../../../../src/components/ha-yaml-editor"; | ||||
| import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor"; | ||||
| import { | ||||
| @@ -34,67 +29,35 @@ import type { HomeAssistant } from "../../../../src/types"; | ||||
| import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart"; | ||||
| import { hassioStyle } from "../../resources/hassio-style"; | ||||
|  | ||||
| const SUPPORTED_UI_TYPES = ["string", "select", "boolean", "integer", "float"]; | ||||
|  | ||||
| @customElement("hassio-addon-config") | ||||
| class HassioAddonConfig extends LitElement { | ||||
|   @property({ attribute: false }) public hass!: HomeAssistant; | ||||
|  | ||||
|   @property({ attribute: false }) public addon!: HassioAddonDetails; | ||||
|  | ||||
|   @property({ attribute: false }) public hass!: HomeAssistant; | ||||
|   @internalProperty() private _error?: string; | ||||
|  | ||||
|   @property({ type: Boolean }) private _configHasChanged = false; | ||||
|  | ||||
|   @property({ type: Boolean }) private _valid = true; | ||||
|  | ||||
|   @internalProperty() private _canShowSchema = false; | ||||
|  | ||||
|   @internalProperty() private _error?: string; | ||||
|  | ||||
|   @internalProperty() private _options?: Record<string, unknown>; | ||||
|  | ||||
|   @internalProperty() private _yamlMode = false; | ||||
|  | ||||
|   @query("ha-yaml-editor") private _editor?: HaYamlEditor; | ||||
|   @query("ha-yaml-editor", true) private _editor!: HaYamlEditor; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     return html` | ||||
|       <h1>${this.addon.name}</h1> | ||||
|       <ha-card> | ||||
|         <div class="header"> | ||||
|           <h2>Configuration</h2> | ||||
|           <div class="card-menu"> | ||||
|             <ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}> | ||||
|               <mwc-icon-button slot="trigger"> | ||||
|                 <ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon> | ||||
|               </mwc-icon-button> | ||||
|               <mwc-list-item .disabled=${!this._canShowSchema}> | ||||
|                 ${this._yamlMode ? "Edit in UI" : "Edit in YAML"} | ||||
|               </mwc-list-item> | ||||
|               <mwc-list-item class="warning"> | ||||
|                 Reset to defaults | ||||
|               </mwc-list-item> | ||||
|             </ha-button-menu> | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
|       <ha-card header="Configuration"> | ||||
|         <div class="card-content"> | ||||
|           ${!this._yamlMode && this._canShowSchema && this.addon.schema | ||||
|             ? html`<ha-form | ||||
|                 .data=${this._options!} | ||||
|                 @value-changed=${this._configChanged} | ||||
|                 .schema=${this.addon.schema} | ||||
|               ></ha-form>` | ||||
|             : html` <ha-yaml-editor | ||||
|                 @value-changed=${this._configChanged} | ||||
|               ></ha-yaml-editor>`} | ||||
|           <ha-yaml-editor | ||||
|             @value-changed=${this._configChanged} | ||||
|           ></ha-yaml-editor> | ||||
|           ${this._error ? html` <div class="errors">${this._error}</div> ` : ""} | ||||
|           ${!this._yamlMode || | ||||
|           (this._canShowSchema && this.addon.schema) || | ||||
|           this._valid | ||||
|             ? "" | ||||
|             : html` <div class="errors">Invalid YAML</div> `} | ||||
|           ${this._valid ? "" : html` <div class="errors">Invalid YAML</div> `} | ||||
|         </div> | ||||
|         <div class="card-actions right"> | ||||
|         <div class="card-actions"> | ||||
|           <ha-progress-button class="warning" @click=${this._resetTapped}> | ||||
|             Reset to defaults | ||||
|           </ha-progress-button> | ||||
|           <ha-progress-button | ||||
|             @click=${this._saveTapped} | ||||
|             .disabled=${!this._configHasChanged || !this._valid} | ||||
| @@ -106,55 +69,16 @@ class HassioAddonConfig extends LitElement { | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProps) { | ||||
|     super.firstUpdated(changedProps); | ||||
|     this._canShowSchema = | ||||
|       Object.keys(this.addon.options).length !== 0 && | ||||
|       !this.addon.schema!.find( | ||||
|         // @ts-ignore | ||||
|         (entry) => !SUPPORTED_UI_TYPES.includes(entry.type) || entry.multiple | ||||
|       ); | ||||
|     this._yamlMode = !this._canShowSchema; | ||||
|   } | ||||
|  | ||||
|   protected updated(changedProperties: PropertyValues): void { | ||||
|     if (changedProperties.has("addon")) { | ||||
|       this._options = { ...this.addon.options }; | ||||
|     } | ||||
|     super.updated(changedProperties); | ||||
|     if ( | ||||
|       changedProperties.has("_yamlMode") || | ||||
|       changedProperties.has("_options") | ||||
|     ) { | ||||
|       if (this._yamlMode) { | ||||
|         const editor = this._editor; | ||||
|         if (editor) { | ||||
|           editor.setValue(this._options!); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private _handleAction(ev: CustomEvent<ActionDetail>) { | ||||
|     switch (ev.detail.index) { | ||||
|       case 0: | ||||
|         this._yamlMode = !this._yamlMode; | ||||
|         break; | ||||
|       case 1: | ||||
|         this._resetTapped(ev); | ||||
|         break; | ||||
|     if (changedProperties.has("addon")) { | ||||
|       this._editor.setValue(this.addon.options); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private _configChanged(ev): void { | ||||
|     if (this.addon.schema && this._canShowSchema && !this._yamlMode) { | ||||
|       this._valid = true; | ||||
|       this._configHasChanged = true; | ||||
|       this._options! = ev.detail.value; | ||||
|     } else { | ||||
|       this._configHasChanged = true; | ||||
|       this._valid = ev.detail.isValid; | ||||
|     } | ||||
|     this._configHasChanged = true; | ||||
|     this._valid = ev.detail.isValid; | ||||
|   } | ||||
|  | ||||
|   private async _resetTapped(ev: CustomEvent): Promise<void> { | ||||
| @@ -198,13 +122,18 @@ class HassioAddonConfig extends LitElement { | ||||
|     const button = ev.currentTarget as any; | ||||
|     button.progress = true; | ||||
|  | ||||
|     let data: HassioAddonSetOptionParams; | ||||
|     this._error = undefined; | ||||
|  | ||||
|     try { | ||||
|       await setHassioAddonOption(this.hass, this.addon.slug, { | ||||
|         options: this._yamlMode ? this._editor?.value : this._options, | ||||
|       }); | ||||
|  | ||||
|       data = { | ||||
|         options: this._editor.value, | ||||
|       }; | ||||
|     } catch (err) { | ||||
|       this._error = err; | ||||
|       return; | ||||
|     } | ||||
|     try { | ||||
|       await setHassioAddonOption(this.hass, this.addon.slug, data); | ||||
|       this._configHasChanged = false; | ||||
|       const eventdata = { | ||||
|         success: true, | ||||
| @@ -249,32 +178,6 @@ class HassioAddonConfig extends LitElement { | ||||
|         .syntaxerror { | ||||
|           color: var(--error-color); | ||||
|         } | ||||
|         .card-menu { | ||||
|           float: right; | ||||
|           z-index: 3; | ||||
|           --mdc-theme-text-primary-on-background: var(--primary-text-color); | ||||
|         } | ||||
|         mwc-list-item[disabled] { | ||||
|           --mdc-theme-text-primary-on-background: var(--disabled-text-color); | ||||
|         } | ||||
|         .header { | ||||
|           display: flex; | ||||
|           justify-content: space-between; | ||||
|         } | ||||
|         .header h2 { | ||||
|           color: var(--ha-card-header-color, --primary-text-color); | ||||
|           font-family: var(--ha-card-header-font-family, inherit); | ||||
|           font-size: var(--ha-card-header-font-size, 24px); | ||||
|           letter-spacing: -0.012em; | ||||
|           line-height: 48px; | ||||
|           padding: 12px 16px 16px; | ||||
|           display: block; | ||||
|           margin-block: 0px; | ||||
|           font-weight: normal; | ||||
|         } | ||||
|         .card-actions.right { | ||||
|           justify-content: flex-end; | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   } | ||||
|   | ||||
| @@ -9,25 +9,17 @@ import { | ||||
|   CSSResult, | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
|   LitElement, | ||||
|   property, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import memoizeOne from "memoize-one"; | ||||
| import { fireEvent } from "../../../src/common/dom/fire_event"; | ||||
| import { navigate } from "../../../src/common/navigate"; | ||||
| import { extractSearchParam } from "../../../src/common/url/search-params"; | ||||
| import "../../../src/components/ha-circular-progress"; | ||||
| import { | ||||
|   fetchHassioAddonInfo, | ||||
|   HassioAddonDetails, | ||||
| } from "../../../src/data/hassio/addon"; | ||||
| import { extractApiErrorMessage } from "../../../src/data/hassio/common"; | ||||
| import { Supervisor } from "../../../src/data/supervisor/supervisor"; | ||||
| import "../../../src/layouts/hass-error-screen"; | ||||
| import "../../../src/layouts/hass-loading-screen"; | ||||
| import "../../../src/layouts/hass-tabs-subpage"; | ||||
| import "../../../src/components/ha-circular-progress"; | ||||
| import type { PageNavigation } from "../../../src/layouts/hass-tabs-subpage"; | ||||
| import { haStyle } from "../../../src/resources/styles"; | ||||
| import { HomeAssistant, Route } from "../../../src/types"; | ||||
| @@ -43,16 +35,12 @@ import "./log/hassio-addon-logs"; | ||||
| class HassioAddonDashboard extends LitElement { | ||||
|   @property({ attribute: false }) public hass!: HomeAssistant; | ||||
|  | ||||
|   @property({ attribute: false }) public supervisor!: Supervisor; | ||||
|  | ||||
|   @property({ attribute: false }) public route!: Route; | ||||
|  | ||||
|   @property({ attribute: false }) public addon?: HassioAddonDetails; | ||||
|  | ||||
|   @property({ type: Boolean }) public narrow!: boolean; | ||||
|  | ||||
|   @internalProperty() _error?: string; | ||||
|  | ||||
|   private _computeTail = memoizeOne((route: Route) => { | ||||
|     const dividerPos = route.path.indexOf("/", 1); | ||||
|     return dividerPos === -1 | ||||
| @@ -67,14 +55,8 @@ class HassioAddonDashboard extends LitElement { | ||||
|   }); | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     if (this._error) { | ||||
|       return html`<hass-error-screen | ||||
|         .error=${this._error} | ||||
|       ></hass-error-screen>`; | ||||
|     } | ||||
|  | ||||
|     if (!this.addon) { | ||||
|       return html`<hass-loading-screen></hass-loading-screen>`; | ||||
|       return html`<ha-circular-progress active></ha-circular-progress>`; | ||||
|     } | ||||
|  | ||||
|     const addonTabs: PageNavigation[] = [ | ||||
| @@ -124,7 +106,6 @@ class HassioAddonDashboard extends LitElement { | ||||
|           .route=${route} | ||||
|           .narrow=${this.narrow} | ||||
|           .hass=${this.hass} | ||||
|           .supervisor=${this.supervisor} | ||||
|           .addon=${this.addon} | ||||
|         ></hassio-addon-router> | ||||
|       </hass-tabs-subpage> | ||||
| @@ -171,51 +152,30 @@ class HassioAddonDashboard extends LitElement { | ||||
|   } | ||||
|  | ||||
|   protected async firstUpdated(): Promise<void> { | ||||
|     if (this.route.path === "") { | ||||
|       const addon = extractSearchParam("addon"); | ||||
|       if (addon) { | ||||
|         navigate(this, `/hassio/addon/${addon}`, true); | ||||
|       } | ||||
|     } | ||||
|     await this._routeDataChanged(this.route); | ||||
|     this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev)); | ||||
|   } | ||||
|  | ||||
|   private async _apiCalled(ev): Promise<void> { | ||||
|     const pathSplit: string[] = ev.detail.path?.split("/"); | ||||
|     const path: string = ev.detail.path; | ||||
|  | ||||
|     if (!pathSplit || pathSplit.length === 0) { | ||||
|     if (!path) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     const path: string = pathSplit[pathSplit.length - 1]; | ||||
|  | ||||
|     if (["uninstall", "install", "update", "start", "stop"].includes(path)) { | ||||
|       fireEvent(this, "supervisor-store-refresh", { store: "supervisor" }); | ||||
|     } | ||||
|  | ||||
|     if (path === "uninstall") { | ||||
|       window.history.back(); | ||||
|       history.back(); | ||||
|     } else { | ||||
|       await this._routeDataChanged(); | ||||
|       await this._routeDataChanged(this.route); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   protected updated(changedProperties) { | ||||
|     if (changedProperties.has("route") && !this.addon) { | ||||
|       this._routeDataChanged(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private async _routeDataChanged(): Promise<void> { | ||||
|     const addon = this.route.path.split("/")[1]; | ||||
|     if (!addon) { | ||||
|       return; | ||||
|     } | ||||
|   private async _routeDataChanged(routeData: Route): Promise<void> { | ||||
|     const addon = routeData.path.split("/")[1]; | ||||
|     try { | ||||
|       const addoninfo = await fetchHassioAddonInfo(this.hass, addon); | ||||
|       this.addon = addoninfo; | ||||
|     } catch (err) { | ||||
|       this._error = `Error fetching addon info: ${extractApiErrorMessage(err)}`; | ||||
|     } catch { | ||||
|       this.addon = undefined; | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| import { customElement, property } from "lit-element"; | ||||
| import { HassioAddonDetails } from "../../../src/data/hassio/addon"; | ||||
| import { Supervisor } from "../../../src/data/supervisor/supervisor"; | ||||
| import { | ||||
|   HassRouterPage, | ||||
|   RouterOptions, | ||||
| @@ -18,8 +17,6 @@ class HassioAddonRouter extends HassRouterPage { | ||||
|  | ||||
|   @property({ attribute: false }) public hass!: HomeAssistant; | ||||
|  | ||||
|   @property({ attribute: false }) public supervisor!: Supervisor; | ||||
|  | ||||
|   @property({ attribute: false }) public addon!: HassioAddonDetails; | ||||
|  | ||||
|   protected routerOptions: RouterOptions = { | ||||
| @@ -44,7 +41,6 @@ class HassioAddonRouter extends HassRouterPage { | ||||
|   protected updatePageEl(el) { | ||||
|     el.route = this.routeTail; | ||||
|     el.hass = this.hass; | ||||
|     el.supervisor = this.supervisor; | ||||
|     el.addon = this.addon; | ||||
|     el.narrow = this.narrow; | ||||
|   } | ||||
|   | ||||
| @@ -7,9 +7,8 @@ import { | ||||
|   property, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import "../../../../src/components/ha-circular-progress"; | ||||
| import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; | ||||
| import { Supervisor } from "../../../../src/data/supervisor/supervisor"; | ||||
| import "../../../../src/components/ha-circular-progress"; | ||||
| import { haStyle } from "../../../../src/resources/styles"; | ||||
| import { HomeAssistant } from "../../../../src/types"; | ||||
| import { hassioStyle } from "../../resources/hassio-style"; | ||||
| @@ -21,8 +20,6 @@ class HassioAddonInfoDashboard extends LitElement { | ||||
|  | ||||
|   @property({ attribute: false }) public hass!: HomeAssistant; | ||||
|  | ||||
|   @property({ attribute: false }) public supervisor!: Supervisor; | ||||
|  | ||||
|   @property({ attribute: false }) public addon?: HassioAddonDetails; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
| @@ -35,7 +32,6 @@ class HassioAddonInfoDashboard extends LitElement { | ||||
|         <hassio-addon-info | ||||
|           .narrow=${this.narrow} | ||||
|           .hass=${this.hass} | ||||
|           .supervisor=${this.supervisor} | ||||
|           .addon=${this.addon} | ||||
|         ></hassio-addon-info> | ||||
|       </div> | ||||
|   | ||||
| @@ -43,33 +43,22 @@ import { | ||||
|   HassioAddonSetOptionParams, | ||||
|   HassioAddonSetSecurityParams, | ||||
|   installHassioAddon, | ||||
|   restartHassioAddon, | ||||
|   setHassioAddonOption, | ||||
|   setHassioAddonSecurity, | ||||
|   startHassioAddon, | ||||
|   stopHassioAddon, | ||||
|   uninstallHassioAddon, | ||||
|   updateHassioAddon, | ||||
|   validateHassioAddonOption, | ||||
| } from "../../../../src/data/hassio/addon"; | ||||
| import { | ||||
|   extractApiErrorMessage, | ||||
|   fetchHassioStats, | ||||
|   HassioStats, | ||||
| } from "../../../../src/data/hassio/common"; | ||||
| import { Supervisor } from "../../../../src/data/supervisor/supervisor"; | ||||
| import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; | ||||
| import { | ||||
|   showAlertDialog, | ||||
|   showConfirmationDialog, | ||||
| } from "../../../../src/dialogs/generic/show-dialog-box"; | ||||
| import { haStyle } from "../../../../src/resources/styles"; | ||||
| import { HomeAssistant } from "../../../../src/types"; | ||||
| import { bytesToString } from "../../../../src/util/bytes-to-string"; | ||||
| import "../../components/hassio-card-content"; | ||||
| import "../../components/supervisor-metric"; | ||||
| import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown"; | ||||
| import { hassioStyle } from "../../resources/hassio-style"; | ||||
| import { addonArchIsSupported } from "../../util/addon"; | ||||
|  | ||||
| const STAGE_ICON = { | ||||
|   stable: mdiCheckCircle, | ||||
| @@ -80,7 +69,7 @@ const STAGE_ICON = { | ||||
| const PERMIS_DESC = { | ||||
|   stage: { | ||||
|     title: "Add-on Stage", | ||||
|     description: `Add-ons can have one of three stages:\n\n<ha-svg-icon path="${STAGE_ICON.stable}"></ha-svg-icon> **Stable**: These are add-ons ready to be used in production.\n\n<ha-svg-icon path="${STAGE_ICON.experimental}"></ha-svg-icon> **Experimental**: These may contain bugs, and may be unfinished.\n\n<ha-svg-icon path="${STAGE_ICON.deprecated}"></ha-svg-icon> **Deprecated**: These add-ons will no longer receive any updates.`, | ||||
|     description: `Add-ons can have one of three stages:\n\n<ha-svg-icon .path='${STAGE_ICON.stable}'></ha-svg-icon> **Stable**: These are add-ons ready to be used in production.\n\n<ha-svg-icon .path='${STAGE_ICON.experimental}'></ha-svg-icon> **Experimental**: These may contain bugs, and may be unfinished.\n\n<ha-svg-icon .path='${STAGE_ICON.deprecated}'></ha-svg-icon> **Deprecated**: These add-ons will no longer receive any updates.`, | ||||
|   }, | ||||
|   rating: { | ||||
|     title: "Add-on Security Rating", | ||||
| @@ -142,26 +131,9 @@ class HassioAddonInfo extends LitElement { | ||||
|  | ||||
|   @property({ attribute: false }) public addon!: HassioAddonDetails; | ||||
|  | ||||
|   @property({ attribute: false }) public supervisor!: Supervisor; | ||||
|  | ||||
|   @internalProperty() private _metrics?: HassioStats; | ||||
|  | ||||
|   @internalProperty() private _error?: string; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     const metrics = [ | ||||
|       { | ||||
|         description: "Add-on CPU Usage", | ||||
|         value: this._metrics?.cpu_percent, | ||||
|       }, | ||||
|       { | ||||
|         description: "Add-on RAM Usage", | ||||
|         value: this._metrics?.memory_percent, | ||||
|         tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString( | ||||
|           this._metrics?.memory_limit | ||||
|         )}`, | ||||
|       }, | ||||
|     ]; | ||||
|     return html` | ||||
|       ${this.addon.update_available | ||||
|         ? html` | ||||
| @@ -177,31 +149,21 @@ class HassioAddonInfo extends LitElement { | ||||
|                   iconClass="update" | ||||
|                 ></hassio-card-content> | ||||
|                 ${!this.addon.available | ||||
|                   ? !addonArchIsSupported( | ||||
|                       this.supervisor.info.supported_arch, | ||||
|                       this.addon.arch | ||||
|                     ) | ||||
|                     ? html` | ||||
|                         <p> | ||||
|                           This add-on is not compatible with the processor of | ||||
|                           your device or the operating system you have installed | ||||
|                           on your device. | ||||
|                         </p> | ||||
|                       ` | ||||
|                     : html` | ||||
|                         <p> | ||||
|                           You are running Home Assistant | ||||
|                           ${this.supervisor.core.version}, to update to this | ||||
|                           version of the add-on you need at least version | ||||
|                           ${this.addon.homeassistant} of Home Assistant | ||||
|                         </p> | ||||
|                       ` | ||||
|                   ? html` | ||||
|                       <p> | ||||
|                         This update is no longer compatible with your system. | ||||
|                       </p> | ||||
|                     ` | ||||
|                   : ""} | ||||
|               </div> | ||||
|               <div class="card-actions"> | ||||
|                 <ha-progress-button @click=${this._updateClicked}> | ||||
|                 <ha-call-api-button | ||||
|                   .hass=${this.hass} | ||||
|                   .disabled=${!this.addon.available} | ||||
|                   path="hassio/addons/${this.addon.slug}/update" | ||||
|                 > | ||||
|                   Update | ||||
|                 </ha-progress-button> | ||||
|                 </ha-call-api-button> | ||||
|                 ${this.addon.changelog | ||||
|                   ? html` | ||||
|                       <mwc-button @click=${this._openChangelog}> | ||||
| @@ -275,378 +237,331 @@ class HassioAddonInfo extends LitElement { | ||||
|             > | ||||
|             for details. | ||||
|           </div> | ||||
|           <div class="addon-container"> | ||||
|             <div> | ||||
|               ${this.addon.logo | ||||
|                 ? html` | ||||
|                     <img | ||||
|                       class="logo" | ||||
|                       src="/api/hassio/addons/${this.addon.slug}/logo" | ||||
|                     /> | ||||
|                   ` | ||||
|                 : ""} | ||||
|               <div class="security"> | ||||
|                 ${this.addon.stage !== "stable" | ||||
|                   ? html` <ha-label-badge | ||||
|                       class=${classMap({ | ||||
|                         yellow: this.addon.stage === "experimental", | ||||
|                         red: this.addon.stage === "deprecated", | ||||
|                       })} | ||||
|                       @click=${this._showMoreInfo} | ||||
|                       id="stage" | ||||
|                       label="stage" | ||||
|                       description="" | ||||
|                     > | ||||
|                       <ha-svg-icon | ||||
|                         .path=${STAGE_ICON[this.addon.stage]} | ||||
|                       ></ha-svg-icon> | ||||
|                     </ha-label-badge>` | ||||
|                   : ""} | ||||
|  | ||||
|                 <ha-label-badge | ||||
|           ${this.addon.logo | ||||
|             ? html` | ||||
|                 <img | ||||
|                   class="logo" | ||||
|                   src="/api/hassio/addons/${this.addon.slug}/logo" | ||||
|                 /> | ||||
|               ` | ||||
|             : ""} | ||||
|           <div class="security"> | ||||
|             ${this.addon.stage !== "stable" | ||||
|               ? html` <ha-label-badge | ||||
|                   class=${classMap({ | ||||
|                     green: [5, 6].includes(Number(this.addon.rating)), | ||||
|                     yellow: [3, 4].includes(Number(this.addon.rating)), | ||||
|                     red: [1, 2].includes(Number(this.addon.rating)), | ||||
|                     yellow: this.addon.stage === "experimental", | ||||
|                     red: this.addon.stage === "deprecated", | ||||
|                   })} | ||||
|                   @click=${this._showMoreInfo} | ||||
|                   id="rating" | ||||
|                   .value=${this.addon.rating} | ||||
|                   label="rating" | ||||
|                   id="stage" | ||||
|                   label="stage" | ||||
|                   description="" | ||||
|                 ></ha-label-badge> | ||||
|                 ${this.addon.host_network | ||||
|                   ? html` | ||||
|                       <ha-label-badge | ||||
|                         @click=${this._showMoreInfo} | ||||
|                         id="host_network" | ||||
|                         label="host" | ||||
|                         description="" | ||||
|                       > | ||||
|                         <ha-svg-icon .path=${mdiNetwork}></ha-svg-icon> | ||||
|                       </ha-label-badge> | ||||
|                     ` | ||||
|                   : ""} | ||||
|                 ${this.addon.full_access | ||||
|                   ? html` | ||||
|                       <ha-label-badge | ||||
|                         @click=${this._showMoreInfo} | ||||
|                         id="full_access" | ||||
|                         label="hardware" | ||||
|                         description="" | ||||
|                       > | ||||
|                         <ha-svg-icon .path=${mdiChip}></ha-svg-icon> | ||||
|                       </ha-label-badge> | ||||
|                     ` | ||||
|                   : ""} | ||||
|                 ${this.addon.homeassistant_api | ||||
|                   ? html` | ||||
|                       <ha-label-badge | ||||
|                         @click=${this._showMoreInfo} | ||||
|                         id="homeassistant_api" | ||||
|                         label="hass" | ||||
|                         description="" | ||||
|                       > | ||||
|                         <ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon> | ||||
|                       </ha-label-badge> | ||||
|                     ` | ||||
|                   : ""} | ||||
|                 ${this._computeHassioApi | ||||
|                   ? html` | ||||
|                       <ha-label-badge | ||||
|                         @click=${this._showMoreInfo} | ||||
|                         id="hassio_api" | ||||
|                         label="hassio" | ||||
|                         .description=${this.addon.hassio_role} | ||||
|                       > | ||||
|                         <ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon> | ||||
|                       </ha-label-badge> | ||||
|                     ` | ||||
|                   : ""} | ||||
|                 ${this.addon.docker_api | ||||
|                   ? html` | ||||
|                       <ha-label-badge | ||||
|                         @click=${this._showMoreInfo} | ||||
|                         id="docker_api" | ||||
|                         label="docker" | ||||
|                         description="" | ||||
|                       > | ||||
|                         <ha-svg-icon .path=${mdiDocker}></ha-svg-icon> | ||||
|                       </ha-label-badge> | ||||
|                     ` | ||||
|                   : ""} | ||||
|                 ${this.addon.host_pid | ||||
|                   ? html` | ||||
|                       <ha-label-badge | ||||
|                         @click=${this._showMoreInfo} | ||||
|                         id="host_pid" | ||||
|                         label="host pid" | ||||
|                         description="" | ||||
|                       > | ||||
|                         <ha-svg-icon .path=${mdiPound}></ha-svg-icon> | ||||
|                       </ha-label-badge> | ||||
|                     ` | ||||
|                   : ""} | ||||
|                 ${this.addon.apparmor | ||||
|                   ? html` | ||||
|                       <ha-label-badge | ||||
|                         @click=${this._showMoreInfo} | ||||
|                         class=${this._computeApparmorClassName} | ||||
|                         id="apparmor" | ||||
|                         label="apparmor" | ||||
|                         description="" | ||||
|                       > | ||||
|                         <ha-svg-icon .path=${mdiShield}></ha-svg-icon> | ||||
|                       </ha-label-badge> | ||||
|                     ` | ||||
|                   : ""} | ||||
|                 ${this.addon.auth_api | ||||
|                   ? html` | ||||
|                       <ha-label-badge | ||||
|                         @click=${this._showMoreInfo} | ||||
|                         id="auth_api" | ||||
|                         label="auth" | ||||
|                         description="" | ||||
|                       > | ||||
|                         <ha-svg-icon .path=${mdiKey}></ha-svg-icon> | ||||
|                       </ha-label-badge> | ||||
|                     ` | ||||
|                   : ""} | ||||
|                 ${this.addon.ingress | ||||
|                   ? html` | ||||
|                       <ha-label-badge | ||||
|                         @click=${this._showMoreInfo} | ||||
|                         id="ingress" | ||||
|                         label="ingress" | ||||
|                         description="" | ||||
|                       > | ||||
|                         <ha-svg-icon | ||||
|                           .path=${mdiCursorDefaultClickOutline} | ||||
|                         ></ha-svg-icon> | ||||
|                       </ha-label-badge> | ||||
|                     ` | ||||
|                   : ""} | ||||
|               </div> | ||||
|                 > | ||||
|                   <ha-svg-icon | ||||
|                     .path=${STAGE_ICON[this.addon.stage]} | ||||
|                   ></ha-svg-icon> | ||||
|                 </ha-label-badge>` | ||||
|               : ""} | ||||
|  | ||||
|               ${this.addon.version | ||||
|                 ? html` | ||||
|                     <div | ||||
|                       class="${classMap({ | ||||
|                         "addon-options": true, | ||||
|                         started: this.addon.state === "started", | ||||
|                       })}" | ||||
|                     > | ||||
|                       <ha-settings-row ?three-line=${this.narrow}> | ||||
|                         <span slot="heading"> | ||||
|                           Start on boot | ||||
|                         </span> | ||||
|                         <span slot="description"> | ||||
|                           Make the add-on start during a system boot | ||||
|                         </span> | ||||
|                         <ha-switch | ||||
|                           @change=${this._startOnBootToggled} | ||||
|                           .checked=${this.addon.boot === "auto"} | ||||
|                           haptic | ||||
|                         ></ha-switch> | ||||
|                       </ha-settings-row> | ||||
|  | ||||
|                       ${this.addon.startup !== "once" | ||||
|                         ? html` | ||||
|                             <ha-settings-row ?three-line=${this.narrow}> | ||||
|                               <span slot="heading"> | ||||
|                                 Watchdog | ||||
|                               </span> | ||||
|                               <span slot="description"> | ||||
|                                 This will start the add-on if it crashes | ||||
|                               </span> | ||||
|                               <ha-switch | ||||
|                                 @change=${this._watchdogToggled} | ||||
|                                 .checked=${this.addon.watchdog} | ||||
|                                 haptic | ||||
|                               ></ha-switch> | ||||
|                             </ha-settings-row> | ||||
|                           ` | ||||
|                         : ""} | ||||
|                       ${this.addon.auto_update || | ||||
|                       this.hass.userData?.showAdvanced | ||||
|                         ? html` | ||||
|                             <ha-settings-row ?three-line=${this.narrow}> | ||||
|                               <span slot="heading"> | ||||
|                                 Auto update | ||||
|                               </span> | ||||
|                               <span slot="description"> | ||||
|                                 Auto update the add-on when there is a new | ||||
|                                 version available | ||||
|                               </span> | ||||
|                               <ha-switch | ||||
|                                 @change=${this._autoUpdateToggled} | ||||
|                                 .checked=${this.addon.auto_update} | ||||
|                                 haptic | ||||
|                               ></ha-switch> | ||||
|                             </ha-settings-row> | ||||
|                           ` | ||||
|                         : ""} | ||||
|                       ${this.addon.ingress | ||||
|                         ? html` | ||||
|                             <ha-settings-row ?three-line=${this.narrow}> | ||||
|                               <span slot="heading"> | ||||
|                                 Show in sidebar | ||||
|                               </span> | ||||
|                               <span slot="description"> | ||||
|                                 ${this._computeCannotIngressSidebar | ||||
|                                   ? "This option requires Home Assistant 0.92 or later." | ||||
|                                   : "Add this add-on to your sidebar"} | ||||
|                               </span> | ||||
|                               <ha-switch | ||||
|                                 @change=${this._panelToggled} | ||||
|                                 .checked=${this.addon.ingress_panel} | ||||
|                                 .disabled=${this._computeCannotIngressSidebar} | ||||
|                                 haptic | ||||
|                               ></ha-switch> | ||||
|                             </ha-settings-row> | ||||
|                           ` | ||||
|                         : ""} | ||||
|                       ${this._computeUsesProtectedOptions | ||||
|                         ? html` | ||||
|                             <ha-settings-row ?three-line=${this.narrow}> | ||||
|                               <span slot="heading"> | ||||
|                                 Protection mode | ||||
|                               </span> | ||||
|                               <span slot="description"> | ||||
|                                 Blocks elevated system access from the add-on | ||||
|                               </span> | ||||
|                               <ha-switch | ||||
|                                 @change=${this._protectionToggled} | ||||
|                                 .checked=${this.addon.protected} | ||||
|                                 haptic | ||||
|                               ></ha-switch> | ||||
|                             </ha-settings-row> | ||||
|                           ` | ||||
|                         : ""} | ||||
|                     </div> | ||||
|                   ` | ||||
|                 : ""} | ||||
|             </div> | ||||
|             <div> | ||||
|               ${this.addon.state === "started" | ||||
|                 ? html`<ha-settings-row ?three-line=${this.narrow}> | ||||
|                       <span slot="heading"> | ||||
|                         Hostname | ||||
|                       </span> | ||||
|                       <code slot="description"> | ||||
|                         ${this.addon.hostname} | ||||
|                       </code> | ||||
|                     </ha-settings-row> | ||||
|                     ${metrics.map( | ||||
|                       (metric) => | ||||
|                         html` | ||||
|                           <supervisor-metric | ||||
|                             .description=${metric.description} | ||||
|                             .value=${metric.value ?? 0} | ||||
|                             .tooltip=${metric.tooltip} | ||||
|                           ></supervisor-metric> | ||||
|                         ` | ||||
|                     )}` | ||||
|                 : ""} | ||||
|             </div> | ||||
|           </div> | ||||
|           ${this._error ? html` <div class="errors">${this._error}</div> ` : ""} | ||||
|           ${!this.addon.available | ||||
|             ? !addonArchIsSupported( | ||||
|                 this.supervisor.info.supported_arch, | ||||
|                 this.addon.arch | ||||
|               ) | ||||
|             <ha-label-badge | ||||
|               class=${classMap({ | ||||
|                 green: [5, 6].includes(Number(this.addon.rating)), | ||||
|                 yellow: [3, 4].includes(Number(this.addon.rating)), | ||||
|                 red: [1, 2].includes(Number(this.addon.rating)), | ||||
|               })} | ||||
|               @click=${this._showMoreInfo} | ||||
|               id="rating" | ||||
|               .value=${this.addon.rating} | ||||
|               label="rating" | ||||
|               description="" | ||||
|             ></ha-label-badge> | ||||
|             ${this.addon.host_network | ||||
|               ? html` | ||||
|                   <p class="warning"> | ||||
|                     This add-on is not compatible with the processor of your | ||||
|                     device or the operating system you have installed on your | ||||
|                     device. | ||||
|                   </p> | ||||
|                 ` | ||||
|               : html` | ||||
|                   <p class="warning"> | ||||
|                     You are running Home Assistant | ||||
|                     ${this.supervisor.core.version}, to install this add-on you | ||||
|                     need at least version ${this.addon.homeassistant} of Home | ||||
|                     Assistant | ||||
|                   </p> | ||||
|                 ` | ||||
|             : ""} | ||||
|         </div> | ||||
|         <div class="card-actions"> | ||||
|           <div> | ||||
|             ${this.addon.version | ||||
|               ? this._computeIsRunning | ||||
|                 ? html` | ||||
|                     <ha-progress-button | ||||
|                       class="warning" | ||||
|                       @click=${this._stopClicked} | ||||
|                     > | ||||
|                       Stop | ||||
|                     </ha-progress-button> | ||||
|                     <ha-progress-button | ||||
|                       class="warning" | ||||
|                       @click=${this._restartClicked} | ||||
|                     > | ||||
|                       Restart | ||||
|                     </ha-progress-button> | ||||
|                   ` | ||||
|                 : html` | ||||
|                     <ha-progress-button @click=${this._startClicked}> | ||||
|                       Start | ||||
|                     </ha-progress-button> | ||||
|                   ` | ||||
|               : html` | ||||
|                   <ha-progress-button | ||||
|                     .disabled=${!this.addon.available} | ||||
|                     @click=${this._installClicked} | ||||
|                   <ha-label-badge | ||||
|                     @click=${this._showMoreInfo} | ||||
|                     id="host_network" | ||||
|                     label="host" | ||||
|                     description="" | ||||
|                   > | ||||
|                     Install | ||||
|                   </ha-progress-button> | ||||
|                 `} | ||||
|           </div> | ||||
|           <div> | ||||
|             ${this.addon.version | ||||
|               ? html` ${this._computeShowWebUI | ||||
|                     ? html` | ||||
|                         <a | ||||
|                           href=${this._pathWebui!} | ||||
|                           tabindex="-1" | ||||
|                           target="_blank" | ||||
|                           rel="noopener" | ||||
|                         > | ||||
|                           <mwc-button> | ||||
|                             Open web UI | ||||
|                           </mwc-button> | ||||
|                         </a> | ||||
|                       ` | ||||
|                     : ""} | ||||
|                   ${this._computeShowIngressUI | ||||
|                     ? html` | ||||
|                         <mwc-button @click=${this._openIngress}> | ||||
|                           Open web UI | ||||
|                         </mwc-button> | ||||
|                       ` | ||||
|                     : ""} | ||||
|                   <ha-progress-button | ||||
|                     class="warning" | ||||
|                     @click=${this._uninstallClicked} | ||||
|                     <ha-svg-icon .path=${mdiNetwork}></ha-svg-icon> | ||||
|                   </ha-label-badge> | ||||
|                 ` | ||||
|               : ""} | ||||
|             ${this.addon.full_access | ||||
|               ? html` | ||||
|                   <ha-label-badge | ||||
|                     @click=${this._showMoreInfo} | ||||
|                     id="full_access" | ||||
|                     label="hardware" | ||||
|                     description="" | ||||
|                   > | ||||
|                     Uninstall | ||||
|                   </ha-progress-button> | ||||
|                   ${this.addon.build | ||||
|                     ? html` | ||||
|                         <ha-call-api-button | ||||
|                           class="warning" | ||||
|                           .hass=${this.hass} | ||||
|                           .path="hassio/addons/${this.addon.slug}/rebuild" | ||||
|                         > | ||||
|                           Rebuild | ||||
|                         </ha-call-api-button> | ||||
|                       ` | ||||
|                     : ""}` | ||||
|                     <ha-svg-icon .path=${mdiChip}></ha-svg-icon> | ||||
|                   </ha-label-badge> | ||||
|                 ` | ||||
|               : ""} | ||||
|             ${this.addon.homeassistant_api | ||||
|               ? html` | ||||
|                   <ha-label-badge | ||||
|                     @click=${this._showMoreInfo} | ||||
|                     id="homeassistant_api" | ||||
|                     label="hass" | ||||
|                     description="" | ||||
|                   > | ||||
|                     <ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon> | ||||
|                   </ha-label-badge> | ||||
|                 ` | ||||
|               : ""} | ||||
|             ${this._computeHassioApi | ||||
|               ? html` | ||||
|                   <ha-label-badge | ||||
|                     @click=${this._showMoreInfo} | ||||
|                     id="hassio_api" | ||||
|                     label="hassio" | ||||
|                     .description=${this.addon.hassio_role} | ||||
|                   > | ||||
|                     <ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon> | ||||
|                   </ha-label-badge> | ||||
|                 ` | ||||
|               : ""} | ||||
|             ${this.addon.docker_api | ||||
|               ? html` | ||||
|                   <ha-label-badge | ||||
|                     @click=${this._showMoreInfo} | ||||
|                     id="docker_api" | ||||
|                     label="docker" | ||||
|                     description="" | ||||
|                   > | ||||
|                     <ha-svg-icon .path=${mdiDocker}></ha-svg-icon> | ||||
|                   </ha-label-badge> | ||||
|                 ` | ||||
|               : ""} | ||||
|             ${this.addon.host_pid | ||||
|               ? html` | ||||
|                   <ha-label-badge | ||||
|                     @click=${this._showMoreInfo} | ||||
|                     id="host_pid" | ||||
|                     label="host pid" | ||||
|                     description="" | ||||
|                   > | ||||
|                     <ha-svg-icon .path=${mdiPound}></ha-svg-icon> | ||||
|                   </ha-label-badge> | ||||
|                 ` | ||||
|               : ""} | ||||
|             ${this.addon.apparmor | ||||
|               ? html` | ||||
|                   <ha-label-badge | ||||
|                     @click=${this._showMoreInfo} | ||||
|                     class=${this._computeApparmorClassName} | ||||
|                     id="apparmor" | ||||
|                     label="apparmor" | ||||
|                     description="" | ||||
|                   > | ||||
|                     <ha-svg-icon .path=${mdiShield}></ha-svg-icon> | ||||
|                   </ha-label-badge> | ||||
|                 ` | ||||
|               : ""} | ||||
|             ${this.addon.auth_api | ||||
|               ? html` | ||||
|                   <ha-label-badge | ||||
|                     @click=${this._showMoreInfo} | ||||
|                     id="auth_api" | ||||
|                     label="auth" | ||||
|                     description="" | ||||
|                   > | ||||
|                     <ha-svg-icon .path=${mdiKey}></ha-svg-icon> | ||||
|                   </ha-label-badge> | ||||
|                 ` | ||||
|               : ""} | ||||
|             ${this.addon.ingress | ||||
|               ? html` | ||||
|                   <ha-label-badge | ||||
|                     @click=${this._showMoreInfo} | ||||
|                     id="ingress" | ||||
|                     label="ingress" | ||||
|                     description="" | ||||
|                   > | ||||
|                     <ha-svg-icon | ||||
|                       .path=${mdiCursorDefaultClickOutline} | ||||
|                     ></ha-svg-icon> | ||||
|                   </ha-label-badge> | ||||
|                 ` | ||||
|               : ""} | ||||
|           </div> | ||||
|  | ||||
|           ${this.addon.version | ||||
|             ? html` | ||||
|                 <div class="addon-options"> | ||||
|                   <ha-settings-row ?three-line=${this.narrow}> | ||||
|                     <span slot="heading"> | ||||
|                       Start on boot | ||||
|                     </span> | ||||
|                     <span slot="description"> | ||||
|                       Make the add-on start during a system boot | ||||
|                     </span> | ||||
|                     <ha-switch | ||||
|                       @change=${this._startOnBootToggled} | ||||
|                       .checked=${this.addon.boot === "auto"} | ||||
|                       haptic | ||||
|                     ></ha-switch> | ||||
|                   </ha-settings-row> | ||||
|  | ||||
|                   ${this.addon.startup !== "once" | ||||
|                     ? html` | ||||
|                         <ha-settings-row ?three-line=${this.narrow}> | ||||
|                           <span slot="heading"> | ||||
|                             Watchdog | ||||
|                           </span> | ||||
|                           <span slot="description"> | ||||
|                             This will start the add-on if it crashes | ||||
|                           </span> | ||||
|                           <ha-switch | ||||
|                             @change=${this._watchdogToggled} | ||||
|                             .checked=${this.addon.watchdog} | ||||
|                             haptic | ||||
|                           ></ha-switch> | ||||
|                         </ha-settings-row> | ||||
|                       ` | ||||
|                     : ""} | ||||
|                   ${this.addon.auto_update || this.hass.userData?.showAdvanced | ||||
|                     ? html` | ||||
|                         <ha-settings-row ?three-line=${this.narrow}> | ||||
|                           <span slot="heading"> | ||||
|                             Auto update | ||||
|                           </span> | ||||
|                           <span slot="description"> | ||||
|                             Auto update the add-on when there is a new version | ||||
|                             available | ||||
|                           </span> | ||||
|                           <ha-switch | ||||
|                             @change=${this._autoUpdateToggled} | ||||
|                             .checked=${this.addon.auto_update} | ||||
|                             haptic | ||||
|                           ></ha-switch> | ||||
|                         </ha-settings-row> | ||||
|                       ` | ||||
|                     : ""} | ||||
|                   ${this.addon.ingress | ||||
|                     ? html` | ||||
|                         <ha-settings-row ?three-line=${this.narrow}> | ||||
|                           <span slot="heading"> | ||||
|                             Show in sidebar | ||||
|                           </span> | ||||
|                           <span slot="description"> | ||||
|                             ${this._computeCannotIngressSidebar | ||||
|                               ? "This option requires Home Assistant 0.92 or later." | ||||
|                               : "Add this add-on to your sidebar"} | ||||
|                           </span> | ||||
|                           <ha-switch | ||||
|                             @change=${this._panelToggled} | ||||
|                             .checked=${this.addon.ingress_panel} | ||||
|                             .disabled=${this._computeCannotIngressSidebar} | ||||
|                             haptic | ||||
|                           ></ha-switch> | ||||
|                         </ha-settings-row> | ||||
|                       ` | ||||
|                     : ""} | ||||
|                   ${this._computeUsesProtectedOptions | ||||
|                     ? html` | ||||
|                         <ha-settings-row ?three-line=${this.narrow}> | ||||
|                           <span slot="heading"> | ||||
|                             Protection mode | ||||
|                           </span> | ||||
|                           <span slot="description"> | ||||
|                             Blocks elevated system access from the add-on | ||||
|                           </span> | ||||
|                           <ha-switch | ||||
|                             @change=${this._protectionToggled} | ||||
|                             .checked=${this.addon.protected} | ||||
|                             haptic | ||||
|                           ></ha-switch> | ||||
|                         </ha-settings-row> | ||||
|                       ` | ||||
|                     : ""} | ||||
|                 </div> | ||||
|               ` | ||||
|             : ""} | ||||
|           ${this._error ? html` <div class="errors">${this._error}</div> ` : ""} | ||||
|         </div> | ||||
|         <div class="card-actions"> | ||||
|           ${this.addon.version | ||||
|             ? html` | ||||
|                 ${this._computeIsRunning | ||||
|                   ? html` | ||||
|                       <ha-call-api-button | ||||
|                         class="warning" | ||||
|                         .hass=${this.hass} | ||||
|                         .path="hassio/addons/${this.addon.slug}/stop" | ||||
|                       > | ||||
|                         Stop | ||||
|                       </ha-call-api-button> | ||||
|                       <ha-call-api-button | ||||
|                         class="warning" | ||||
|                         .hass=${this.hass} | ||||
|                         .path="hassio/addons/${this.addon.slug}/restart" | ||||
|                       > | ||||
|                         Restart | ||||
|                       </ha-call-api-button> | ||||
|                     ` | ||||
|                   : html` | ||||
|                       <ha-progress-button @click=${this._startClicked}> | ||||
|                         Start | ||||
|                       </ha-progress-button> | ||||
|                     `} | ||||
|                 ${this._computeShowWebUI | ||||
|                   ? html` | ||||
|                       <a | ||||
|                         href=${this._pathWebui!} | ||||
|                         tabindex="-1" | ||||
|                         target="_blank" | ||||
|                         class="right" | ||||
|                         rel="noopener" | ||||
|                       > | ||||
|                         <mwc-button> | ||||
|                           Open web UI | ||||
|                         </mwc-button> | ||||
|                       </a> | ||||
|                     ` | ||||
|                   : ""} | ||||
|                 ${this._computeShowIngressUI | ||||
|                   ? html` | ||||
|                       <mwc-button class="right" @click=${this._openIngress}> | ||||
|                         Open web UI | ||||
|                       </mwc-button> | ||||
|                     ` | ||||
|                   : ""} | ||||
|                 <ha-progress-button | ||||
|                   class=" right warning" | ||||
|                   @click=${this._uninstallClicked} | ||||
|                 > | ||||
|                   Uninstall | ||||
|                 </ha-progress-button> | ||||
|                 ${this.addon.build | ||||
|                   ? html` | ||||
|                       <ha-call-api-button | ||||
|                         class="warning right" | ||||
|                         .hass=${this.hass} | ||||
|                         .path="hassio/addons/${this.addon.slug}/rebuild" | ||||
|                       > | ||||
|                         Rebuild | ||||
|                       </ha-call-api-button> | ||||
|                     ` | ||||
|                   : ""} | ||||
|               ` | ||||
|             : html` | ||||
|                 ${!this.addon.available | ||||
|                   ? html` | ||||
|                       <p class="warning"> | ||||
|                         This add-on is not available on your system. | ||||
|                       </p> | ||||
|                     ` | ||||
|                   : ""} | ||||
|                 <ha-progress-button | ||||
|                   .disabled=${!this.addon.available} | ||||
|                   @click=${this._installClicked} | ||||
|                 > | ||||
|                   Install | ||||
|                 </ha-progress-button> | ||||
|               `} | ||||
|         </div> | ||||
|       </ha-card> | ||||
|  | ||||
| @@ -664,22 +579,6 @@ class HassioAddonInfo extends LitElement { | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   protected updated(changedProps) { | ||||
|     super.updated(changedProps); | ||||
|     if (changedProps.has("addon")) { | ||||
|       this._loadData(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private async _loadData(): Promise<void> { | ||||
|     if (this.addon.state === "started") { | ||||
|       this._metrics = await fetchHassioStats( | ||||
|         this.hass, | ||||
|         `addons/${this.addon.slug}` | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private get _computeHassioApi(): boolean { | ||||
|     return ( | ||||
|       this.addon.hassio_api && | ||||
| @@ -880,82 +779,6 @@ class HassioAddonInfo extends LitElement { | ||||
|     button.progress = false; | ||||
|   } | ||||
|  | ||||
|   private async _stopClicked(ev: CustomEvent): Promise<void> { | ||||
|     const button = ev.currentTarget as any; | ||||
|     button.progress = true; | ||||
|  | ||||
|     try { | ||||
|       await stopHassioAddon(this.hass, this.addon.slug); | ||||
|       const eventdata = { | ||||
|         success: true, | ||||
|         response: undefined, | ||||
|         path: "stop", | ||||
|       }; | ||||
|       fireEvent(this, "hass-api-called", eventdata); | ||||
|     } catch (err) { | ||||
|       showAlertDialog(this, { | ||||
|         title: "Failed to stop addon", | ||||
|         text: extractApiErrorMessage(err), | ||||
|       }); | ||||
|     } | ||||
|     button.progress = false; | ||||
|   } | ||||
|  | ||||
|   private async _restartClicked(ev: CustomEvent): Promise<void> { | ||||
|     const button = ev.currentTarget as any; | ||||
|     button.progress = true; | ||||
|  | ||||
|     try { | ||||
|       await restartHassioAddon(this.hass, this.addon.slug); | ||||
|       const eventdata = { | ||||
|         success: true, | ||||
|         response: undefined, | ||||
|         path: "stop", | ||||
|       }; | ||||
|       fireEvent(this, "hass-api-called", eventdata); | ||||
|     } catch (err) { | ||||
|       showAlertDialog(this, { | ||||
|         title: "Failed to restart addon", | ||||
|         text: extractApiErrorMessage(err), | ||||
|       }); | ||||
|     } | ||||
|     button.progress = false; | ||||
|   } | ||||
|  | ||||
|   private async _updateClicked(ev: CustomEvent): Promise<void> { | ||||
|     const button = ev.currentTarget as any; | ||||
|     button.progress = true; | ||||
|  | ||||
|     const confirmed = await showConfirmationDialog(this, { | ||||
|       title: this.addon.name, | ||||
|       text: "Are you sure you want to update this add-on?", | ||||
|       confirmText: "update add-on", | ||||
|       dismissText: "no", | ||||
|     }); | ||||
|  | ||||
|     if (!confirmed) { | ||||
|       button.progress = false; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     this._error = undefined; | ||||
|     try { | ||||
|       await updateHassioAddon(this.hass, this.addon.slug); | ||||
|       const eventdata = { | ||||
|         success: true, | ||||
|         response: undefined, | ||||
|         path: "update", | ||||
|       }; | ||||
|       fireEvent(this, "hass-api-called", eventdata); | ||||
|     } catch (err) { | ||||
|       showAlertDialog(this, { | ||||
|         title: "Failed to update addon", | ||||
|         text: extractApiErrorMessage(err), | ||||
|       }); | ||||
|     } | ||||
|     button.progress = false; | ||||
|   } | ||||
|  | ||||
|   private async _startClicked(ev: CustomEvent): Promise<void> { | ||||
|     const button = ev.currentTarget as any; | ||||
|     button.progress = true; | ||||
| @@ -964,10 +787,10 @@ class HassioAddonInfo extends LitElement { | ||||
|         this.hass, | ||||
|         this.addon.slug | ||||
|       ); | ||||
|       if (!validate.valid) { | ||||
|       if (!validate.data.valid) { | ||||
|         await showConfirmationDialog(this, { | ||||
|           title: "Failed to start addon - configuration validation failed!", | ||||
|           text: validate.message.split(" Got ")[0], | ||||
|           text: validate.data.message.split(" Got ")[0], | ||||
|           confirm: () => this._openConfiguration(), | ||||
|           confirmText: "Go to configuration", | ||||
|           dismissText: "Cancel", | ||||
| @@ -987,12 +810,6 @@ class HassioAddonInfo extends LitElement { | ||||
|     try { | ||||
|       await startHassioAddon(this.hass, this.addon.slug); | ||||
|       this.addon = await fetchHassioAddonInfo(this.hass, this.addon.slug); | ||||
|       const eventdata = { | ||||
|         success: true, | ||||
|         response: undefined, | ||||
|         path: "start", | ||||
|       }; | ||||
|       fireEvent(this, "hass-api-called", eventdata); | ||||
|     } catch (err) { | ||||
|       showAlertDialog(this, { | ||||
|         title: "Failed to start addon", | ||||
| @@ -1108,6 +925,9 @@ class HassioAddonInfo extends LitElement { | ||||
|           font-weight: 500; | ||||
|           color: var(--primary-color); | ||||
|         } | ||||
|         .right { | ||||
|           float: right; | ||||
|         } | ||||
|         protection-enable mwc-button { | ||||
|           --mdc-theme-primary: white; | ||||
|         } | ||||
| @@ -1130,8 +950,7 @@ class HassioAddonInfo extends LitElement { | ||||
|           margin-bottom: 16px; | ||||
|         } | ||||
|         .card-actions { | ||||
|           justify-content: space-between; | ||||
|           display: flex; | ||||
|           display: flow-root; | ||||
|         } | ||||
|         .security h3 { | ||||
|           margin-bottom: 8px; | ||||
| @@ -1167,26 +986,12 @@ class HassioAddonInfo extends LitElement { | ||||
|         } | ||||
|  | ||||
|         .addon-options { | ||||
|           max-width: 90%; | ||||
|           max-width: 50%; | ||||
|         } | ||||
|  | ||||
|         .addon-container { | ||||
|           display: grid; | ||||
|           grid-auto-flow: column; | ||||
|           grid-template-columns: 60% 40%; | ||||
|         } | ||||
|  | ||||
|         .addon-container > div:last-of-type { | ||||
|           align-self: end; | ||||
|         } | ||||
|  | ||||
|         @media (max-width: 720px) { | ||||
|           .addon-options { | ||||
|             max-width: 100%; | ||||
|           } | ||||
|           .addon-container { | ||||
|             display: block; | ||||
|           } | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   | ||||
| @@ -7,8 +7,8 @@ import { | ||||
|   property, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import "../../../../src/components/ha-circular-progress"; | ||||
| import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; | ||||
| import "../../../../src/components/ha-circular-progress"; | ||||
| import { haStyle } from "../../../../src/resources/styles"; | ||||
| import { HomeAssistant } from "../../../../src/types"; | ||||
| import { hassioStyle } from "../../resources/hassio-style"; | ||||
|   | ||||
| @@ -1,87 +0,0 @@ | ||||
| import { | ||||
|   css, | ||||
|   CSSResult, | ||||
|   customElement, | ||||
|   html, | ||||
|   LitElement, | ||||
|   property, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { classMap } from "lit-html/directives/class-map"; | ||||
| import "../../../src/components/ha-bar"; | ||||
| import "../../../src/components/ha-settings-row"; | ||||
| import { roundWithOneDecimal } from "../../../src/util/calculate"; | ||||
|  | ||||
| @customElement("supervisor-metric") | ||||
| class SupervisorMetric extends LitElement { | ||||
|   @property({ type: Number }) public value!: number; | ||||
|  | ||||
|   @property({ type: String }) public description!: string; | ||||
|  | ||||
|   @property({ type: String }) public tooltip?: string; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     const roundedValue = roundWithOneDecimal(this.value); | ||||
|     return html`<ha-settings-row> | ||||
|       <span slot="heading"> | ||||
|         ${this.description} | ||||
|       </span> | ||||
|       <div slot="description" title="${this.tooltip ?? ""}"> | ||||
|         <span class="value"> | ||||
|           ${roundedValue}% | ||||
|         </span> | ||||
|         <ha-bar | ||||
|           class="${classMap({ | ||||
|             "target-warning": roundedValue > 50, | ||||
|             "target-critical": roundedValue > 85, | ||||
|           })}" | ||||
|           .value=${this.value} | ||||
|         ></ha-bar> | ||||
|       </div> | ||||
|     </ha-settings-row>`; | ||||
|   } | ||||
|  | ||||
|   static get styles(): CSSResult { | ||||
|     return css` | ||||
|       ha-settings-row { | ||||
|         padding: 0; | ||||
|         height: 54px; | ||||
|         width: 100%; | ||||
|       } | ||||
|       ha-settings-row > div[slot="description"] { | ||||
|         white-space: normal; | ||||
|         color: var(--secondary-text-color); | ||||
|         display: flex; | ||||
|         justify-content: space-between; | ||||
|       } | ||||
|       ha-bar { | ||||
|         --ha-bar-primary-color: var( | ||||
|           --hassio-bar-ok-color, | ||||
|           var(--success-color) | ||||
|         ); | ||||
|       } | ||||
|       .target-warning { | ||||
|         --ha-bar-primary-color: var( | ||||
|           --hassio-bar-warning-color, | ||||
|           var(--warning-color) | ||||
|         ); | ||||
|       } | ||||
|       .target-critical { | ||||
|         --ha-bar-primary-color: var( | ||||
|           --hassio-bar-critical-color, | ||||
|           var(--error-color) | ||||
|         ); | ||||
|       } | ||||
|       .value { | ||||
|         width: 42px; | ||||
|         padding-right: 4px; | ||||
|       } | ||||
|     `; | ||||
|   } | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   interface HTMLElementTagNameMap { | ||||
|     "supervisor-metric": SupervisorMetric; | ||||
|   } | ||||
| } | ||||
| @@ -10,7 +10,6 @@ import { | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import memoizeOne from "memoize-one"; | ||||
| import { fireEvent } from "../../../src/common/dom/fire_event"; | ||||
| import "../../../src/components/buttons/ha-progress-button"; | ||||
| import "../../../src/components/ha-card"; | ||||
| import "../../../src/components/ha-svg-icon"; | ||||
| @@ -65,7 +64,6 @@ export class HassioUpdate extends LitElement { | ||||
|         <div class="card-group"> | ||||
|           ${this._renderUpdateCard( | ||||
|             "Home Assistant Core", | ||||
|             "core", | ||||
|             this.supervisor.core, | ||||
|             "hassio/homeassistant/update", | ||||
|             `https://${ | ||||
| @@ -74,15 +72,15 @@ export class HassioUpdate extends LitElement { | ||||
|           )} | ||||
|           ${this._renderUpdateCard( | ||||
|             "Supervisor", | ||||
|             "supervisor", | ||||
|             this.supervisor.supervisor, | ||||
|             "hassio/supervisor/update", | ||||
|             `https://github.com//home-assistant/hassio/releases/tag/${this.supervisor.supervisor.version_latest}` | ||||
|             `https://github.com//home-assistant/hassio/releases/tag/${ | ||||
|               this.supervisor.supervisor.version_latest | ||||
|             }` | ||||
|           )} | ||||
|           ${this.supervisor.host.features.includes("hassos") | ||||
|             ? this._renderUpdateCard( | ||||
|                 "Operating System", | ||||
|                 "os", | ||||
|                 this.supervisor.os, | ||||
|                 "hassio/os/update", | ||||
|                 `https://github.com//home-assistant/hassos/releases/tag/${this.supervisor.os.version_latest}` | ||||
| @@ -95,7 +93,6 @@ export class HassioUpdate extends LitElement { | ||||
|  | ||||
|   private _renderUpdateCard( | ||||
|     name: string, | ||||
|     key: string, | ||||
|     object: HassioHomeAssistantInfo | HassioSupervisorInfo | HassioHassOSInfo, | ||||
|     apiPath: string, | ||||
|     releaseNotesUrl: string | ||||
| @@ -121,7 +118,6 @@ export class HassioUpdate extends LitElement { | ||||
|           <ha-progress-button | ||||
|             .apiPath=${apiPath} | ||||
|             .name=${name} | ||||
|             .key=${key} | ||||
|             .version=${object.version_latest} | ||||
|             @click=${this._confirmUpdate} | ||||
|           > | ||||
| @@ -148,7 +144,6 @@ export class HassioUpdate extends LitElement { | ||||
|     } | ||||
|     try { | ||||
|       await this.hass.callApi<HassioResponse<void>>("POST", item.apiPath); | ||||
|       fireEvent(this, "supervisor-store-refresh", { store: item.key }); | ||||
|     } catch (err) { | ||||
|       // Only show an error if the status code was not expected (user behind proxy) | ||||
|       // or no status at all(connection terminated) | ||||
|   | ||||
| @@ -3,9 +3,9 @@ import { | ||||
|   CSSResult, | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
|   LitElement, | ||||
|   property, | ||||
|   internalProperty, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { createCloseHeading } from "../../../../src/components/ha-dialog"; | ||||
|   | ||||
| @@ -22,11 +22,7 @@ import { | ||||
|   fetchHassioSnapshotInfo, | ||||
|   HassioSnapshotDetail, | ||||
| } from "../../../../src/data/hassio/snapshot"; | ||||
| import { Supervisor } from "../../../../src/data/supervisor/supervisor"; | ||||
| import { | ||||
|   showAlertDialog, | ||||
|   showConfirmationDialog, | ||||
| } from "../../../../src/dialogs/generic/show-dialog-box"; | ||||
| import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box"; | ||||
| import { PolymerChangedEvent } from "../../../../src/polymer-types"; | ||||
| import { haStyle, haStyleDialog } from "../../../../src/resources/styles"; | ||||
| import { HomeAssistant } from "../../../../src/types"; | ||||
| @@ -79,8 +75,6 @@ interface FolderItem { | ||||
| class HassioSnapshotDialog extends LitElement { | ||||
|   @property({ attribute: false }) public hass!: HomeAssistant; | ||||
|  | ||||
|   @property({ attribute: false }) public supervisor?: Supervisor; | ||||
|  | ||||
|   @internalProperty() private _error?: string; | ||||
|  | ||||
|   @internalProperty() private _onboarding = false; | ||||
| @@ -108,7 +102,6 @@ class HassioSnapshotDialog extends LitElement { | ||||
|  | ||||
|     this._dialogParams = params; | ||||
|     this._onboarding = params.onboarding ?? false; | ||||
|     this.supervisor = params.supervisor; | ||||
|   } | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
| @@ -305,16 +298,6 @@ class HassioSnapshotDialog extends LitElement { | ||||
|   } | ||||
|  | ||||
|   private async _partialRestoreClicked() { | ||||
|     if ( | ||||
|       this.supervisor !== undefined && | ||||
|       this.supervisor.info.state !== "running" | ||||
|     ) { | ||||
|       await showAlertDialog(this, { | ||||
|         title: "Could not restore snapshot", | ||||
|         text: `Restoring a snapshot is not possible right now because the system is in ${this.supervisor.info.state} state.`, | ||||
|       }); | ||||
|       return; | ||||
|     } | ||||
|     if ( | ||||
|       !(await showConfirmationDialog(this, { | ||||
|         title: "Are you sure you want partially to restore this snapshot?", | ||||
| @@ -376,16 +359,6 @@ class HassioSnapshotDialog extends LitElement { | ||||
|   } | ||||
|  | ||||
|   private async _fullRestoreClicked() { | ||||
|     if ( | ||||
|       this.supervisor !== undefined && | ||||
|       this.supervisor.info.state !== "running" | ||||
|     ) { | ||||
|       await showAlertDialog(this, { | ||||
|         title: "Could not restore snapshot", | ||||
|         text: `Restoring a snapshot is not possible right now because the system is in ${this.supervisor.info.state} state.`, | ||||
|       }); | ||||
|       return; | ||||
|     } | ||||
|     if ( | ||||
|       !(await showConfirmationDialog(this, { | ||||
|         title: | ||||
|   | ||||
| @@ -1,11 +1,9 @@ | ||||
| import { fireEvent } from "../../../../src/common/dom/fire_event"; | ||||
| import { Supervisor } from "../../../../src/data/supervisor/supervisor"; | ||||
|  | ||||
| export interface HassioSnapshotDialogParams { | ||||
|   slug: string; | ||||
|   onDelete?: () => void; | ||||
|   onboarding?: boolean; | ||||
|   supervisor?: Supervisor; | ||||
| } | ||||
|  | ||||
| export const showHassioSnapshotDialog = ( | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| // Compat needs to be first import | ||||
| import "../../src/resources/compatibility"; | ||||
| import "../../src/resources/roboto"; | ||||
| import "../../src/resources/safari-14-attachshadow-patch"; | ||||
| import "../../src/resources/roboto"; | ||||
| import "./hassio-main"; | ||||
|  | ||||
| const styleEl = document.createElement("style"); | ||||
|   | ||||
| @@ -1,13 +1,11 @@ | ||||
| import { customElement, html, property, PropertyValues } from "lit-element"; | ||||
| import { atLeastVersion } from "../../src/common/config/version"; | ||||
| import { html, PropertyValues, customElement, property } from "lit-element"; | ||||
| import "./hassio-router"; | ||||
| import { HomeAssistant, Route } from "../../src/types"; | ||||
| import { HassioPanelInfo } from "../../src/data/hassio/supervisor"; | ||||
| import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element"; | ||||
| import { fireEvent } from "../../src/common/dom/fire_event"; | ||||
| import { HassioPanelInfo } from "../../src/data/hassio/supervisor"; | ||||
| import { supervisorStore } from "../../src/data/supervisor/supervisor"; | ||||
| import { makeDialogManager } from "../../src/dialogs/make-dialog-manager"; | ||||
| import "../../src/layouts/hass-loading-screen"; | ||||
| import { HomeAssistant, Route } from "../../src/types"; | ||||
| import "./hassio-router"; | ||||
| import { atLeastVersion } from "../../src/common/config/version"; | ||||
| import { SupervisorBaseElement } from "./supervisor-base-element"; | ||||
|  | ||||
| @customElement("hassio-main") | ||||
| @@ -73,15 +71,8 @@ export class HassioMain extends SupervisorBaseElement { | ||||
|  | ||||
|   protected render() { | ||||
|     if (!this.supervisor || !this.hass) { | ||||
|       return html`<hass-loading-screen></hass-loading-screen>`; | ||||
|       return html``; | ||||
|     } | ||||
|  | ||||
|     if ( | ||||
|       Object.keys(supervisorStore).some((store) => !this.supervisor![store]) | ||||
|     ) { | ||||
|       return html`<hass-loading-screen></hass-loading-screen>`; | ||||
|     } | ||||
|  | ||||
|     return html` | ||||
|       <hassio-router | ||||
|         .hass=${this.hass} | ||||
|   | ||||
| @@ -1,128 +0,0 @@ | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
|   LitElement, | ||||
|   property, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { sanitizeUrl } from "@braintree/sanitize-url"; | ||||
| import { | ||||
|   createSearchParam, | ||||
|   extractSearchParamsObject, | ||||
| } from "../../src/common/url/search-params"; | ||||
| import "../../src/layouts/hass-error-screen"; | ||||
| import { | ||||
|   ParamType, | ||||
|   Redirect, | ||||
|   Redirects, | ||||
| } from "../../src/panels/my/ha-panel-my"; | ||||
| import { navigate } from "../../src/common/navigate"; | ||||
| import { HomeAssistant, Route } from "../../src/types"; | ||||
|  | ||||
| const REDIRECTS: Redirects = { | ||||
|   supervisor_logs: { | ||||
|     redirect: "/hassio/system", | ||||
|   }, | ||||
|   supervisor_info: { | ||||
|     redirect: "/hassio/system", | ||||
|   }, | ||||
|   supervisor_snapshots: { | ||||
|     redirect: "/hassio/snapshots", | ||||
|   }, | ||||
|   supervisor_store: { | ||||
|     redirect: "/hassio/store", | ||||
|   }, | ||||
|   supervisor: { | ||||
|     redirect: "/hassio/dashboard", | ||||
|   }, | ||||
|   supervisor_addon: { | ||||
|     redirect: "/hassio/addon", | ||||
|     params: { | ||||
|       addon: "string", | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| @customElement("hassio-my-redirect") | ||||
| class HassioMyRedirect extends LitElement { | ||||
|   @property({ attribute: false }) public hass!: HomeAssistant; | ||||
|  | ||||
|   @property() public route!: Route; | ||||
|  | ||||
|   @internalProperty() public _error?: TemplateResult | string; | ||||
|  | ||||
|   connectedCallback() { | ||||
|     super.connectedCallback(); | ||||
|     const path = this.route.path.substr(1); | ||||
|     const redirect = REDIRECTS[path]; | ||||
|  | ||||
|     if (!redirect) { | ||||
|       this._error = html`This redirect is not supported by your Home Assistant | ||||
|         instance. Check the | ||||
|         <a | ||||
|           target="_blank" | ||||
|           rel="noreferrer noopener" | ||||
|           href="https://my.home-assistant.io/faq.html#supported-pages" | ||||
|           >My Home Assistant FAQ</a | ||||
|         > | ||||
|         for the supported redirects and the version they where introduced.`; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     let url: string; | ||||
|     try { | ||||
|       url = this._createRedirectUrl(redirect); | ||||
|     } catch (err) { | ||||
|       this._error = "An unknown error occured"; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     navigate(this, url, true); | ||||
|   } | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     if (this._error) { | ||||
|       return html`<hass-error-screen | ||||
|         .error=${this._error} | ||||
|       ></hass-error-screen>`; | ||||
|     } | ||||
|     return html``; | ||||
|   } | ||||
|  | ||||
|   private _createRedirectUrl(redirect: Redirect): string { | ||||
|     const params = this._createRedirectParams(redirect); | ||||
|     return `${redirect.redirect}${params}`; | ||||
|   } | ||||
|  | ||||
|   private _createRedirectParams(redirect: Redirect): string { | ||||
|     const params = extractSearchParamsObject(); | ||||
|     if (!redirect.params && !Object.keys(params).length) { | ||||
|       return ""; | ||||
|     } | ||||
|     const resultParams = {}; | ||||
|     Object.entries(redirect.params || {}).forEach(([key, type]) => { | ||||
|       if (!params[key] || !this._checkParamType(type, params[key])) { | ||||
|         throw Error(); | ||||
|       } | ||||
|       resultParams[key] = params[key]; | ||||
|     }); | ||||
|     return `?${createSearchParam(resultParams)}`; | ||||
|   } | ||||
|  | ||||
|   private _checkParamType(type: ParamType, value: string) { | ||||
|     if (type === "string") { | ||||
|       return true; | ||||
|     } | ||||
|     if (type === "url") { | ||||
|       return value && value === sanitizeUrl(value); | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   interface HTMLElementTagNameMap { | ||||
|     "hassio-my-redirect": HassioMyRedirect; | ||||
|   } | ||||
| } | ||||
| @@ -41,10 +41,6 @@ class HassioRouter extends HassRouterPage { | ||||
|         tag: "hassio-ingress-view", | ||||
|         load: () => import("./ingress-view/hassio-ingress-view"), | ||||
|       }, | ||||
|       _my_redirect: { | ||||
|         tag: "hassio-my-redirect", | ||||
|         load: () => import("./hassio-my-redirect"), | ||||
|       }, | ||||
|     }, | ||||
|   }; | ||||
|  | ||||
| @@ -53,13 +49,12 @@ class HassioRouter extends HassRouterPage { | ||||
|     const route = el.nodeName === "HASSIO-PANEL" ? this.route : this.routeTail; | ||||
|  | ||||
|     el.hass = this.hass; | ||||
|     el.supervisor = this.supervisor; | ||||
|     el.narrow = this.narrow; | ||||
|     el.route = route; | ||||
|  | ||||
|     if (el.localName === "hassio-ingress-view") { | ||||
|       el.ingressPanel = this.panel.config && this.panel.config.ingress; | ||||
|     } else { | ||||
|       el.supervisor = this.supervisor; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -1,17 +1,14 @@ | ||||
| import { mdiMenu } from "@mdi/js"; | ||||
| import { | ||||
|   css, | ||||
|   CSSResult, | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
|   LitElement, | ||||
|   property, | ||||
|   internalProperty, | ||||
|   PropertyValues, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { fireEvent } from "../../../src/common/dom/fire_event"; | ||||
| import { navigate } from "../../../src/common/navigate"; | ||||
| import { | ||||
|   fetchHassioAddonInfo, | ||||
|   HassioAddonDetails, | ||||
| @@ -20,10 +17,13 @@ import { | ||||
|   createHassioSession, | ||||
|   validateHassioSession, | ||||
| } from "../../../src/data/hassio/ingress"; | ||||
| import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box"; | ||||
| import "../../../src/layouts/hass-loading-screen"; | ||||
| import "../../../src/layouts/hass-subpage"; | ||||
| import { HomeAssistant, Route } from "../../../src/types"; | ||||
| import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box"; | ||||
| import { navigate } from "../../../src/common/navigate"; | ||||
| import { mdiMenu } from "@mdi/js"; | ||||
| import { fireEvent } from "../../../src/common/dom/fire_event"; | ||||
|  | ||||
| @customElement("hassio-ingress-view") | ||||
| class HassioIngressView extends LitElement { | ||||
|   | ||||
| @@ -41,7 +41,6 @@ import { | ||||
|   reloadHassioSnapshots, | ||||
| } from "../../../src/data/hassio/snapshot"; | ||||
| import { Supervisor } from "../../../src/data/supervisor/supervisor"; | ||||
| import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box"; | ||||
| import "../../../src/layouts/hass-tabs-subpage"; | ||||
| import { PolymerChangedEvent } from "../../../src/polymer-types"; | ||||
| import { haStyle } from "../../../src/resources/styles"; | ||||
| @@ -212,13 +211,7 @@ class HassioSnapshots extends LitElement { | ||||
|                   : undefined} | ||||
|               </div> | ||||
|               <div class="card-actions"> | ||||
|                 <ha-progress-button | ||||
|                   @click=${this._createSnapshot} | ||||
|                   title="${this.supervisor.info.state !== "running" | ||||
|                     ? `Creating a snapshot is not possible right now because the system is in ${this.supervisor.info.state} state.` | ||||
|                     : ""}" | ||||
|                   .disabled=${this.supervisor.info.state !== "running"} | ||||
|                 > | ||||
|                 <ha-progress-button @click=${this._createSnapshot}> | ||||
|                   Create | ||||
|                 </ha-progress-button> | ||||
|               </div> | ||||
| @@ -271,7 +264,7 @@ class HassioSnapshots extends LitElement { | ||||
|   } | ||||
|  | ||||
|   protected updated(changedProps: PropertyValues) { | ||||
|     if (changedProps.has("supervisor")) { | ||||
|     if (changedProps.has("supervisorInfo")) { | ||||
|       this._addonList = this.supervisor.supervisor.addons | ||||
|         .map((addon) => ({ | ||||
|           slug: addon.slug, | ||||
| @@ -332,12 +325,6 @@ class HassioSnapshots extends LitElement { | ||||
|   } | ||||
|  | ||||
|   private async _createSnapshot(ev: CustomEvent): Promise<void> { | ||||
|     if (this.supervisor.info.state !== "running") { | ||||
|       await showAlertDialog(this, { | ||||
|         title: "Could not create snapshot", | ||||
|         text: `Creating a snapshot is not possible right now because the system is in ${this.supervisor.info.state} state.`, | ||||
|       }); | ||||
|     } | ||||
|     const button = ev.currentTarget as any; | ||||
|     button.progress = true; | ||||
|  | ||||
| @@ -399,7 +386,6 @@ class HassioSnapshots extends LitElement { | ||||
|   private _snapshotClicked(ev) { | ||||
|     showHassioSnapshotDialog(this, { | ||||
|       slug: ev.currentTarget!.snapshot.slug, | ||||
|       supervisor: this.supervisor, | ||||
|       onDelete: () => this._updateSnapshots(), | ||||
|     }); | ||||
|   } | ||||
| @@ -409,7 +395,6 @@ class HassioSnapshots extends LitElement { | ||||
|       showSnapshot: (slug: string) => | ||||
|         showHassioSnapshotDialog(this, { | ||||
|           slug, | ||||
|           supervisor: this.supervisor, | ||||
|           onDelete: () => this._updateSnapshots(), | ||||
|         }), | ||||
|       reloadSnapshot: () => this.refreshData(), | ||||
|   | ||||
| @@ -1,16 +1,4 @@ | ||||
| import { Collection, UnsubscribeFunc } from "home-assistant-js-websocket"; | ||||
| import { | ||||
|   internalProperty, | ||||
|   LitElement, | ||||
|   property, | ||||
|   PropertyValues, | ||||
| } from "lit-element"; | ||||
| import { atLeastVersion } from "../../src/common/config/version"; | ||||
| import { fetchHassioAddonsInfo } from "../../src/data/hassio/addon"; | ||||
| import { | ||||
|   hassioApiResultExtractor, | ||||
|   HassioResponse, | ||||
| } from "../../src/data/hassio/common"; | ||||
| import { LitElement, property, PropertyValues } from "lit-element"; | ||||
| import { | ||||
|   fetchHassioHassOsInfo, | ||||
|   fetchHassioHostInfo, | ||||
| @@ -22,23 +10,13 @@ import { | ||||
|   fetchHassioInfo, | ||||
|   fetchHassioSupervisorInfo, | ||||
| } from "../../src/data/hassio/supervisor"; | ||||
| import { | ||||
|   getSupervisorEventCollection, | ||||
|   subscribeSupervisorEvents, | ||||
|   Supervisor, | ||||
|   supervisorApiRequest, | ||||
|   SupervisorAPIRequestParams, | ||||
|   supervisorApiWsRequest, | ||||
|   SupervisorObject, | ||||
|   supervisorStore, | ||||
| } from "../../src/data/supervisor/supervisor"; | ||||
| import { Supervisor } from "../../src/data/supervisor/supervisor"; | ||||
| import { ProvideHassLitMixin } from "../../src/mixins/provide-hass-lit-mixin"; | ||||
| import { urlSyncMixin } from "../../src/state/url-sync-mixin"; | ||||
|  | ||||
| declare global { | ||||
|   interface HASSDomEvents { | ||||
|     "supervisor-update": Partial<Supervisor>; | ||||
|     "supervisor-store-refresh": { store: SupervisorObject }; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -47,84 +25,20 @@ export class SupervisorBaseElement extends urlSyncMixin( | ||||
| ) { | ||||
|   @property({ attribute: false }) public supervisor?: Supervisor; | ||||
|  | ||||
|   @internalProperty() private _unsubs: Record<string, UnsubscribeFunc> = {}; | ||||
|  | ||||
|   @internalProperty() private _collections: Record< | ||||
|     string, | ||||
|     Collection<unknown> | ||||
|   > = {}; | ||||
|  | ||||
|   public disconnectedCallback() { | ||||
|     super.disconnectedCallback(); | ||||
|     Object.keys(this._unsubs).forEach((unsub) => { | ||||
|       this._unsubs[unsub](); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   protected _updateSupervisor(obj: Partial<Supervisor>): void { | ||||
|     this.supervisor = { | ||||
|       ...this.supervisor!, | ||||
|       ...obj, | ||||
|       callApi: (params) => this._callAPI(params), | ||||
|     }; | ||||
|     this.supervisor = { ...this.supervisor!, ...obj }; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(changedProps: PropertyValues): void { | ||||
|     super.firstUpdated(changedProps); | ||||
|     this._initSupervisor(); | ||||
|   } | ||||
|  | ||||
|   private async _handleSupervisorStoreRefreshEvent(ev) { | ||||
|     const store = ev.detail.store; | ||||
|     if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) { | ||||
|       this._collections[store].refresh(); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     const response = await this.hass.callApi<HassioResponse<any>>( | ||||
|       "GET", | ||||
|       `hassio${supervisorStore[store]}` | ||||
|     this.addEventListener("supervisor-update", (ev) => | ||||
|       this._updateSupervisor(ev.detail) | ||||
|     ); | ||||
|     this._updateSupervisor({ [store]: response.data }); | ||||
|   } | ||||
|  | ||||
|   private async _initSupervisor(): Promise<void> { | ||||
|     this.addEventListener( | ||||
|       "supervisor-store-refresh", | ||||
|       this._handleSupervisorStoreRefreshEvent | ||||
|     ); | ||||
|  | ||||
|     if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) { | ||||
|       Object.keys(supervisorStore).forEach((store) => { | ||||
|         this._unsubs[store] = subscribeSupervisorEvents( | ||||
|           this.hass, | ||||
|           (data) => this._updateSupervisor({ [store]: data }), | ||||
|           store, | ||||
|           supervisorStore[store] | ||||
|         ); | ||||
|         if (this._collections[store]) { | ||||
|           this._collections[store].refresh(); | ||||
|         } else { | ||||
|           this._collections[store] = getSupervisorEventCollection( | ||||
|             this.hass.connection, | ||||
|             store, | ||||
|             supervisorStore[store] | ||||
|           ); | ||||
|         } | ||||
|       }); | ||||
|  | ||||
|       if (this.supervisor === undefined) { | ||||
|         Object.keys(this._collections).forEach((collection) => | ||||
|           this._updateSupervisor({ | ||||
|             [collection]: this._collections[collection].state, | ||||
|           }) | ||||
|         ); | ||||
|       } | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     const [ | ||||
|       addon, | ||||
|       supervisor, | ||||
|       host, | ||||
|       core, | ||||
| @@ -133,7 +47,6 @@ export class SupervisorBaseElement extends urlSyncMixin( | ||||
|       network, | ||||
|       resolution, | ||||
|     ] = await Promise.all([ | ||||
|       fetchHassioAddonsInfo(this.hass), | ||||
|       fetchHassioSupervisorInfo(this.hass), | ||||
|       fetchHassioHostInfo(this.hass), | ||||
|       fetchHassioHomeAssistantInfo(this.hass), | ||||
| @@ -144,7 +57,6 @@ export class SupervisorBaseElement extends urlSyncMixin( | ||||
|     ]); | ||||
|  | ||||
|     this.supervisor = { | ||||
|       addon, | ||||
|       supervisor, | ||||
|       host, | ||||
|       core, | ||||
| @@ -152,48 +64,6 @@ export class SupervisorBaseElement extends urlSyncMixin( | ||||
|       os, | ||||
|       network, | ||||
|       resolution, | ||||
|       callApi: (params) => this._callAPI(params), | ||||
|     }; | ||||
|  | ||||
|     this.addEventListener("supervisor-update", (ev) => | ||||
|       this._updateSupervisor(ev.detail) | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   private async _callAPI<T>(params: SupervisorAPIRequestParams): Promise<T> { | ||||
|     const hasHass = this.hass !== undefined; | ||||
|     const canUseWS = | ||||
|       !params.rest && | ||||
|       hasHass && | ||||
|       atLeastVersion(this.hass.config.version, 2021, 2, 4); | ||||
|  | ||||
|     if (canUseWS) { | ||||
|       const connection = hasHass ? this.hass.connection : params.connection; | ||||
|       if (connection === undefined) { | ||||
|         throw Error(`No connection found, aborting API call - ${params}`); | ||||
|       } | ||||
|       const requestParams: supervisorApiRequest = { | ||||
|         ...params, | ||||
|       }; | ||||
|       delete requestParams.rest; | ||||
|       delete requestParams.connection; | ||||
|       return await supervisorApiWsRequest<T>(connection, requestParams); | ||||
|     } else { | ||||
|       const method = | ||||
|         params.method === "post" | ||||
|           ? "POST" | ||||
|           : params.method === "put" | ||||
|           ? "PUT" | ||||
|           : params.method === "delete" | ||||
|           ? "DELETE" | ||||
|           : "GET"; | ||||
|       return hassioApiResultExtractor<T>( | ||||
|         await this.hass.callApi<HassioResponse<T>>( | ||||
|           method, | ||||
|           `hassio${params.endpoint}`, | ||||
|           params.data | ||||
|         ) | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,248 +0,0 @@ | ||||
| import "@material/mwc-button"; | ||||
| import "@material/mwc-list/mwc-list-item"; | ||||
| import { | ||||
|   css, | ||||
|   CSSResult, | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
|   LitElement, | ||||
|   property, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { fireEvent } from "../../../src/common/dom/fire_event"; | ||||
| import "../../../src/components/buttons/ha-progress-button"; | ||||
| import "../../../src/components/ha-button-menu"; | ||||
| import "../../../src/components/ha-card"; | ||||
| import "../../../src/components/ha-settings-row"; | ||||
| import { | ||||
|   extractApiErrorMessage, | ||||
|   fetchHassioStats, | ||||
|   HassioStats, | ||||
| } from "../../../src/data/hassio/common"; | ||||
| import { restartCore, updateCore } from "../../../src/data/supervisor/core"; | ||||
| import { Supervisor } from "../../../src/data/supervisor/supervisor"; | ||||
| import { | ||||
|   showAlertDialog, | ||||
|   showConfirmationDialog, | ||||
| } from "../../../src/dialogs/generic/show-dialog-box"; | ||||
| import { haStyle } from "../../../src/resources/styles"; | ||||
| import { HomeAssistant } from "../../../src/types"; | ||||
| import { bytesToString } from "../../../src/util/bytes-to-string"; | ||||
| import "../components/supervisor-metric"; | ||||
| import { hassioStyle } from "../resources/hassio-style"; | ||||
|  | ||||
| @customElement("hassio-core-info") | ||||
| class HassioCoreInfo extends LitElement { | ||||
|   @property({ attribute: false }) public hass!: HomeAssistant; | ||||
|  | ||||
|   @property({ attribute: false }) public supervisor!: Supervisor; | ||||
|  | ||||
|   @internalProperty() private _metrics?: HassioStats; | ||||
|  | ||||
|   protected render(): TemplateResult | void { | ||||
|     const metrics = [ | ||||
|       { | ||||
|         description: "Core CPU Usage", | ||||
|         value: this._metrics?.cpu_percent, | ||||
|       }, | ||||
|       { | ||||
|         description: "Core RAM Usage", | ||||
|         value: this._metrics?.memory_percent, | ||||
|         tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString( | ||||
|           this._metrics?.memory_limit | ||||
|         )}`, | ||||
|       }, | ||||
|     ]; | ||||
|  | ||||
|     return html` | ||||
|       <ha-card header="Core"> | ||||
|         <div class="card-content"> | ||||
|           <div> | ||||
|             <ha-settings-row> | ||||
|               <span slot="heading"> | ||||
|                 Version | ||||
|               </span> | ||||
|               <span slot="description"> | ||||
|                 core-${this.supervisor.core.version} | ||||
|               </span> | ||||
|             </ha-settings-row> | ||||
|             <ha-settings-row> | ||||
|               <span slot="heading"> | ||||
|                 Newest Version | ||||
|               </span> | ||||
|               <span slot="description"> | ||||
|                 core-${this.supervisor.core.version_latest} | ||||
|               </span> | ||||
|               ${this.supervisor.core.update_available | ||||
|                 ? html` | ||||
|                     <ha-progress-button | ||||
|                       title="Update the core" | ||||
|                       @click=${this._coreUpdate} | ||||
|                     > | ||||
|                       Update | ||||
|                     </ha-progress-button> | ||||
|                   ` | ||||
|                 : ""} | ||||
|             </ha-settings-row> | ||||
|           </div> | ||||
|           <div> | ||||
|             ${metrics.map( | ||||
|               (metric) => | ||||
|                 html` | ||||
|                   <supervisor-metric | ||||
|                     .description=${metric.description} | ||||
|                     .value=${metric.value ?? 0} | ||||
|                     .tooltip=${metric.tooltip} | ||||
|                   ></supervisor-metric> | ||||
|                 ` | ||||
|             )} | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="card-actions"> | ||||
|           <ha-progress-button | ||||
|             slot="primaryAction" | ||||
|             class="warning" | ||||
|             @click=${this._coreRestart} | ||||
|             title="Restart Home Assistant Core" | ||||
|           > | ||||
|             Restart Core | ||||
|           </ha-progress-button> | ||||
|         </div> | ||||
|       </ha-card> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(): void { | ||||
|     this._loadData(); | ||||
|   } | ||||
|  | ||||
|   private async _loadData(): Promise<void> { | ||||
|     this._metrics = await fetchHassioStats(this.hass, "core"); | ||||
|   } | ||||
|  | ||||
|   private async _coreRestart(ev: CustomEvent): Promise<void> { | ||||
|     const button = ev.currentTarget as any; | ||||
|     button.progress = true; | ||||
|  | ||||
|     const confirmed = await showConfirmationDialog(this, { | ||||
|       title: "Restart Home Assistant Core", | ||||
|       text: "Are you sure you want to restart Home Assistant Core", | ||||
|       confirmText: "restart", | ||||
|       dismissText: "cancel", | ||||
|     }); | ||||
|  | ||||
|     if (!confirmed) { | ||||
|       button.progress = false; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|       await restartCore(this.hass); | ||||
|     } catch (err) { | ||||
|       showAlertDialog(this, { | ||||
|         title: "Failed to restart Home Assistant Core", | ||||
|         text: extractApiErrorMessage(err), | ||||
|       }); | ||||
|     } finally { | ||||
|       button.progress = false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private async _coreUpdate(ev: CustomEvent): Promise<void> { | ||||
|     const button = ev.currentTarget as any; | ||||
|     button.progress = true; | ||||
|  | ||||
|     const confirmed = await showConfirmationDialog(this, { | ||||
|       title: "Update Home Assistant Core", | ||||
|       text: `Are you sure you want to update Home Assistant Core to version ${this.supervisor.core.version_latest}?`, | ||||
|       confirmText: "update", | ||||
|       dismissText: "cancel", | ||||
|     }); | ||||
|  | ||||
|     if (!confirmed) { | ||||
|       button.progress = false; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|       await updateCore(this.hass); | ||||
|       fireEvent(this, "supervisor-store-refresh", { store: "core" }); | ||||
|     } catch (err) { | ||||
|       showAlertDialog(this, { | ||||
|         title: "Failed to update Home Assistant Core", | ||||
|         text: extractApiErrorMessage(err), | ||||
|       }); | ||||
|     } finally { | ||||
|       button.progress = false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static get styles(): CSSResult[] { | ||||
|     return [ | ||||
|       haStyle, | ||||
|       hassioStyle, | ||||
|       css` | ||||
|         ha-card { | ||||
|           height: 100%; | ||||
|           justify-content: space-between; | ||||
|           flex-direction: column; | ||||
|           display: flex; | ||||
|         } | ||||
|         .card-actions { | ||||
|           height: 48px; | ||||
|           border-top: none; | ||||
|           display: flex; | ||||
|           justify-content: flex-end; | ||||
|           align-items: center; | ||||
|         } | ||||
|         .card-content { | ||||
|           display: flex; | ||||
|           flex-direction: column; | ||||
|           height: calc(100% - 124px); | ||||
|           justify-content: space-between; | ||||
|         } | ||||
|         ha-settings-row { | ||||
|           padding: 0; | ||||
|           height: 54px; | ||||
|           width: 100%; | ||||
|         } | ||||
|         ha-settings-row[three-line] { | ||||
|           height: 74px; | ||||
|         } | ||||
|         ha-settings-row > span[slot="description"] { | ||||
|           white-space: normal; | ||||
|           color: var(--secondary-text-color); | ||||
|         } | ||||
|  | ||||
|         .warning { | ||||
|           --mdc-theme-primary: var(--error-color); | ||||
|         } | ||||
|  | ||||
|         ha-button-menu { | ||||
|           color: var(--secondary-text-color); | ||||
|           --mdc-menu-min-width: 200px; | ||||
|         } | ||||
|         @media (min-width: 563px) { | ||||
|           paper-listbox { | ||||
|             max-height: 150px; | ||||
|             overflow: auto; | ||||
|           } | ||||
|         } | ||||
|         paper-item { | ||||
|           cursor: pointer; | ||||
|           min-height: 35px; | ||||
|         } | ||||
|         mwc-list-item ha-svg-icon { | ||||
|           color: var(--secondary-text-color); | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   } | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   interface HTMLElementTagNameMap { | ||||
|     "hassio-core-info": HassioCoreInfo; | ||||
|   } | ||||
| } | ||||
| @@ -13,7 +13,6 @@ import { | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import memoizeOne from "memoize-one"; | ||||
| import { atLeastVersion } from "../../../src/common/config/version"; | ||||
| import { fireEvent } from "../../../src/common/dom/fire_event"; | ||||
| import "../../../src/components/buttons/ha-progress-button"; | ||||
| import "../../../src/components/ha-button-menu"; | ||||
| @@ -27,6 +26,7 @@ import { fetchHassioHardwareInfo } from "../../../src/data/hassio/hardware"; | ||||
| import { | ||||
|   changeHostOptions, | ||||
|   configSyncOS, | ||||
|   fetchHassioHostInfo, | ||||
|   rebootHost, | ||||
|   shutdownHost, | ||||
|   updateOS, | ||||
| @@ -43,11 +43,6 @@ import { | ||||
| } from "../../../src/dialogs/generic/show-dialog-box"; | ||||
| import { haStyle } from "../../../src/resources/styles"; | ||||
| import { HomeAssistant } from "../../../src/types"; | ||||
| import { | ||||
|   getValueInPercentage, | ||||
|   roundWithOneDecimal, | ||||
| } from "../../../src/util/calculate"; | ||||
| import "../components/supervisor-metric"; | ||||
| import { showHassioMarkdownDialog } from "../dialogs/markdown/show-dialog-hassio-markdown"; | ||||
| import { showNetworkDialog } from "../dialogs/network/show-dialog-network"; | ||||
| import { hassioStyle } from "../resources/hassio-style"; | ||||
| @@ -62,117 +57,80 @@ class HassioHostInfo extends LitElement { | ||||
|     const primaryIpAddress = this.supervisor.host.features.includes("network") | ||||
|       ? this._primaryIpAddress(this.supervisor.network!) | ||||
|       : ""; | ||||
|  | ||||
|     const metrics = [ | ||||
|       { | ||||
|         description: "Used Space", | ||||
|         value: this._getUsedSpace( | ||||
|           this.supervisor.host.disk_used, | ||||
|           this.supervisor.host.disk_total | ||||
|         ), | ||||
|         tooltip: `${this.supervisor.host.disk_used} GB/${this.supervisor.host.disk_total} GB`, | ||||
|       }, | ||||
|     ]; | ||||
|     return html` | ||||
|       <ha-card header="Host"> | ||||
|       <ha-card header="Host System"> | ||||
|         <div class="card-content"> | ||||
|           <div> | ||||
|             ${this.supervisor.host.features.includes("hostname") | ||||
|               ? html`<ha-settings-row> | ||||
|                   <span slot="heading"> | ||||
|                     Hostname | ||||
|                   </span> | ||||
|                   <span slot="description"> | ||||
|                     ${this.supervisor.host.hostname} | ||||
|                   </span> | ||||
|                   <mwc-button | ||||
|                     title="Change the hostname" | ||||
|                     label="Change" | ||||
|                     @click=${this._changeHostnameClicked} | ||||
|                   > | ||||
|                   </mwc-button> | ||||
|                 </ha-settings-row>` | ||||
|               : ""} | ||||
|             ${this.supervisor.host.features.includes("network") | ||||
|               ? html` <ha-settings-row> | ||||
|                   <span slot="heading"> | ||||
|                     IP Address | ||||
|                   </span> | ||||
|                   <span slot="description"> | ||||
|                     ${primaryIpAddress} | ||||
|                   </span> | ||||
|                   <mwc-button | ||||
|                     title="Change the network" | ||||
|                     label="Change" | ||||
|                     @click=${this._changeNetworkClicked} | ||||
|                   > | ||||
|                   </mwc-button> | ||||
|                 </ha-settings-row>` | ||||
|               : ""} | ||||
|           ${this.supervisor.host.features.includes("hostname") | ||||
|             ? html`<ha-settings-row> | ||||
|                 <span slot="heading"> | ||||
|                   Hostname | ||||
|                 </span> | ||||
|                 <span slot="description"> | ||||
|                   ${this.supervisor.host.hostname} | ||||
|                 </span> | ||||
|                 <mwc-button | ||||
|                   title="Change the hostname" | ||||
|                   label="Change" | ||||
|                   @click=${this._changeHostnameClicked} | ||||
|                 > | ||||
|                 </mwc-button> | ||||
|               </ha-settings-row>` | ||||
|             : ""} | ||||
|           ${this.supervisor.host.features.includes("network") | ||||
|             ? html` <ha-settings-row> | ||||
|                 <span slot="heading"> | ||||
|                   IP Address | ||||
|                 </span> | ||||
|                 <span slot="description"> | ||||
|                   ${primaryIpAddress} | ||||
|                 </span> | ||||
|                 <mwc-button | ||||
|                   title="Change the network" | ||||
|                   label="Change" | ||||
|                   @click=${this._changeNetworkClicked} | ||||
|                 > | ||||
|                 </mwc-button> | ||||
|               </ha-settings-row>` | ||||
|             : ""} | ||||
|  | ||||
|             <ha-settings-row> | ||||
|               <span slot="heading"> | ||||
|                 Operating System | ||||
|               </span> | ||||
|               <span slot="description"> | ||||
|                 ${this.supervisor.host.operating_system} | ||||
|               </span> | ||||
|               ${this.supervisor.os.update_available | ||||
|                 ? html` | ||||
|                     <ha-progress-button | ||||
|                       title="Update the host OS" | ||||
|                       @click=${this._osUpdate} | ||||
|                     > | ||||
|                       Update | ||||
|                     </ha-progress-button> | ||||
|                   ` | ||||
|                 : ""} | ||||
|             </ha-settings-row> | ||||
|             ${!this.supervisor.host.features.includes("hassos") | ||||
|               ? html`<ha-settings-row> | ||||
|                   <span slot="heading"> | ||||
|                     Docker version | ||||
|                   </span> | ||||
|                   <span slot="description"> | ||||
|                     ${this.supervisor.info.docker} | ||||
|                   </span> | ||||
|                 </ha-settings-row>` | ||||
|               : ""} | ||||
|             ${this.supervisor.host.deployment | ||||
|               ? html`<ha-settings-row> | ||||
|                   <span slot="heading"> | ||||
|                     Deployment | ||||
|                   </span> | ||||
|                   <span slot="description"> | ||||
|                     ${this.supervisor.host.deployment} | ||||
|                   </span> | ||||
|                 </ha-settings-row>` | ||||
|               : ""} | ||||
|           </div> | ||||
|           <div> | ||||
|             ${this.supervisor.host.disk_life_time !== "" && | ||||
|             this.supervisor.host.disk_life_time >= 10 | ||||
|               ? html` <ha-settings-row> | ||||
|                   <span slot="heading"> | ||||
|                     eMMC Lifetime Used | ||||
|                   </span> | ||||
|                   <span slot="description"> | ||||
|                     ${this.supervisor.host.disk_life_time - 10}% - | ||||
|                     ${this.supervisor.host.disk_life_time}% | ||||
|                   </span> | ||||
|                 </ha-settings-row>` | ||||
|               : ""} | ||||
|             ${metrics.map( | ||||
|               (metric) => | ||||
|                 html` | ||||
|                   <supervisor-metric | ||||
|                     .description=${metric.description} | ||||
|                     .value=${metric.value ?? 0} | ||||
|                     .tooltip=${metric.tooltip} | ||||
|                   ></supervisor-metric> | ||||
|           <ha-settings-row> | ||||
|             <span slot="heading"> | ||||
|               Operating System | ||||
|             </span> | ||||
|             <span slot="description"> | ||||
|               ${this.supervisor.host.operating_system} | ||||
|             </span> | ||||
|             ${this.supervisor.os.update_available | ||||
|               ? html` | ||||
|                   <ha-progress-button | ||||
|                     title="Update the host OS" | ||||
|                     @click=${this._osUpdate} | ||||
|                   > | ||||
|                     Update | ||||
|                   </ha-progress-button> | ||||
|                 ` | ||||
|             )} | ||||
|           </div> | ||||
|               : ""} | ||||
|           </ha-settings-row> | ||||
|           ${!this.supervisor.host.features.includes("hassos") | ||||
|             ? html`<ha-settings-row> | ||||
|                 <span slot="heading"> | ||||
|                   Docker version | ||||
|                 </span> | ||||
|                 <span slot="description"> | ||||
|                   ${this.supervisor.info.docker} | ||||
|                 </span> | ||||
|               </ha-settings-row>` | ||||
|             : ""} | ||||
|           ${this.supervisor.host.deployment | ||||
|             ? html`<ha-settings-row> | ||||
|                 <span slot="heading"> | ||||
|                   Deployment | ||||
|                 </span> | ||||
|                 <span slot="description"> | ||||
|                   ${this.supervisor.host.deployment} | ||||
|                 </span> | ||||
|               </ha-settings-row>` | ||||
|             : ""} | ||||
|         </div> | ||||
|         <div class="card-actions"> | ||||
|           ${this.supervisor.host.features.includes("reboot") | ||||
| @@ -182,7 +140,7 @@ class HassioHostInfo extends LitElement { | ||||
|                   class="warning" | ||||
|                   @click=${this._hostReboot} | ||||
|                 > | ||||
|                   Reboot Host | ||||
|                   Reboot | ||||
|                 </ha-progress-button> | ||||
|               ` | ||||
|             : ""} | ||||
| @@ -193,7 +151,7 @@ class HassioHostInfo extends LitElement { | ||||
|                   class="warning" | ||||
|                   @click=${this._hostShutdown} | ||||
|                 > | ||||
|                   Shutdown Host | ||||
|                   Shutdown | ||||
|                 </ha-progress-button> | ||||
|               ` | ||||
|             : ""} | ||||
| @@ -225,10 +183,6 @@ class HassioHostInfo extends LitElement { | ||||
|     this._loadData(); | ||||
|   } | ||||
|  | ||||
|   private _getUsedSpace = memoizeOne((used: number, total: number) => | ||||
|     roundWithOneDecimal(getValueInPercentage(used, 0, total)) | ||||
|   ); | ||||
|  | ||||
|   private _primaryIpAddress = memoizeOne((network_info: NetworkInfo) => { | ||||
|     if (!network_info || !network_info.interfaces) { | ||||
|       return ""; | ||||
| @@ -340,7 +294,6 @@ class HassioHostInfo extends LitElement { | ||||
|  | ||||
|     try { | ||||
|       await updateOS(this.hass); | ||||
|       fireEvent(this, "supervisor-store-refresh", { store: "os" }); | ||||
|     } catch (err) { | ||||
|       showAlertDialog(this, { | ||||
|         title: "Failed to update", | ||||
| @@ -369,7 +322,8 @@ class HassioHostInfo extends LitElement { | ||||
|     if (hostname && hostname !== curHostname) { | ||||
|       try { | ||||
|         await changeHostOptions(this.hass, { hostname }); | ||||
|         fireEvent(this, "supervisor-store-refresh", { store: "host" }); | ||||
|         const host = await fetchHassioHostInfo(this.hass); | ||||
|         fireEvent(this, "supervisor-update", { host }); | ||||
|       } catch (err) { | ||||
|         showAlertDialog(this, { | ||||
|           title: "Setting hostname failed", | ||||
| @@ -382,7 +336,8 @@ class HassioHostInfo extends LitElement { | ||||
|   private async _importFromUSB(): Promise<void> { | ||||
|     try { | ||||
|       await configSyncOS(this.hass); | ||||
|       fireEvent(this, "supervisor-store-refresh", { store: "host" }); | ||||
|       const host = await fetchHassioHostInfo(this.hass); | ||||
|       fireEvent(this, "supervisor-update", { host }); | ||||
|     } catch (err) { | ||||
|       showAlertDialog(this, { | ||||
|         title: "Failed to import from USB", | ||||
| @@ -392,12 +347,8 @@ class HassioHostInfo extends LitElement { | ||||
|   } | ||||
|  | ||||
|   private async _loadData(): Promise<void> { | ||||
|     if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) { | ||||
|       fireEvent(this, "supervisor-store-refresh", { store: "network" }); | ||||
|     } else { | ||||
|       const network = await fetchNetworkInfo(this.hass); | ||||
|       fireEvent(this, "supervisor-update", { network }); | ||||
|     } | ||||
|     const network = await fetchNetworkInfo(this.hass); | ||||
|     fireEvent(this, "supervisor-update", { network }); | ||||
|   } | ||||
|  | ||||
|   static get styles(): CSSResult[] { | ||||
| @@ -418,12 +369,6 @@ class HassioHostInfo extends LitElement { | ||||
|           justify-content: space-between; | ||||
|           align-items: center; | ||||
|         } | ||||
|         .card-content { | ||||
|           display: flex; | ||||
|           flex-direction: column; | ||||
|           height: calc(100% - 124px); | ||||
|           justify-content: space-between; | ||||
|         } | ||||
|         ha-settings-row { | ||||
|           padding: 0; | ||||
|           height: 54px; | ||||
|   | ||||
| @@ -3,7 +3,6 @@ import { | ||||
|   CSSResult, | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
|   LitElement, | ||||
|   property, | ||||
|   TemplateResult, | ||||
| @@ -13,12 +12,9 @@ import "../../../src/components/buttons/ha-progress-button"; | ||||
| import "../../../src/components/ha-card"; | ||||
| import "../../../src/components/ha-settings-row"; | ||||
| import "../../../src/components/ha-switch"; | ||||
| import { extractApiErrorMessage } from "../../../src/data/hassio/common"; | ||||
| import { | ||||
|   extractApiErrorMessage, | ||||
|   fetchHassioStats, | ||||
|   HassioStats, | ||||
| } from "../../../src/data/hassio/common"; | ||||
| import { | ||||
|   fetchHassioSupervisorInfo, | ||||
|   reloadSupervisor, | ||||
|   restartSupervisor, | ||||
|   setSupervisorOption, | ||||
| @@ -32,9 +28,7 @@ import { | ||||
| } from "../../../src/dialogs/generic/show-dialog-box"; | ||||
| import { haStyle } from "../../../src/resources/styles"; | ||||
| import { HomeAssistant } from "../../../src/types"; | ||||
| import { bytesToString } from "../../../src/util/bytes-to-string"; | ||||
| import { documentationUrl } from "../../../src/util/documentation-url"; | ||||
| import "../components/supervisor-metric"; | ||||
| import { hassioStyle } from "../resources/hassio-style"; | ||||
|  | ||||
| const UNSUPPORTED_REASON = { | ||||
| @@ -93,164 +87,127 @@ class HassioSupervisorInfo extends LitElement { | ||||
|  | ||||
|   @property({ attribute: false }) public supervisor!: Supervisor; | ||||
|  | ||||
|   @internalProperty() private _metrics?: HassioStats; | ||||
|  | ||||
|   protected render(): TemplateResult | void { | ||||
|     const metrics = [ | ||||
|       { | ||||
|         description: "Supervisor CPU Usage", | ||||
|         value: this._metrics?.cpu_percent, | ||||
|       }, | ||||
|       { | ||||
|         description: "Supervisor RAM Usage", | ||||
|         value: this._metrics?.memory_percent, | ||||
|         tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString( | ||||
|           this._metrics?.memory_limit | ||||
|         )}`, | ||||
|       }, | ||||
|     ]; | ||||
|     return html` | ||||
|       <ha-card header="Supervisor"> | ||||
|         <div class="card-content"> | ||||
|           <div> | ||||
|             <ha-settings-row> | ||||
|               <span slot="heading"> | ||||
|                 Version | ||||
|               </span> | ||||
|               <span slot="description"> | ||||
|                 supervisor-${this.supervisor.supervisor.version} | ||||
|               </span> | ||||
|             </ha-settings-row> | ||||
|             <ha-settings-row> | ||||
|               <span slot="heading"> | ||||
|                 Newest Version | ||||
|               </span> | ||||
|               <span slot="description"> | ||||
|                 supervisor-${this.supervisor.supervisor.version_latest} | ||||
|               </span> | ||||
|               ${this.supervisor.supervisor.update_available | ||||
|                 ? html` | ||||
|                     <ha-progress-button | ||||
|                       title="Update the supervisor" | ||||
|                       @click=${this._supervisorUpdate} | ||||
|                     > | ||||
|                       Update | ||||
|                     </ha-progress-button> | ||||
|                   ` | ||||
|                 : ""} | ||||
|             </ha-settings-row> | ||||
|             <ha-settings-row> | ||||
|               <span slot="heading"> | ||||
|                 Channel | ||||
|               </span> | ||||
|               <span slot="description"> | ||||
|                 ${this.supervisor.supervisor.channel} | ||||
|               </span> | ||||
|               ${this.supervisor.supervisor.channel === "beta" | ||||
|                 ? html` | ||||
|                     <ha-progress-button | ||||
|                       @click=${this._toggleBeta} | ||||
|                       title="Get stable updates for Home Assistant, supervisor and host" | ||||
|                     > | ||||
|                       Leave beta channel | ||||
|                     </ha-progress-button> | ||||
|                   ` | ||||
|                 : this.supervisor.supervisor.channel === "stable" | ||||
|                 ? html` | ||||
|                     <ha-progress-button | ||||
|                       @click=${this._toggleBeta} | ||||
|                       title="Get beta updates for Home Assistant (RCs), supervisor and host" | ||||
|                     > | ||||
|                       Join beta channel | ||||
|                     </ha-progress-button> | ||||
|                   ` | ||||
|                 : ""} | ||||
|             </ha-settings-row> | ||||
|  | ||||
|             ${this.supervisor.supervisor.supported | ||||
|               ? html` <ha-settings-row three-line> | ||||
|                   <span slot="heading"> | ||||
|                     Share Diagnostics | ||||
|                   </span> | ||||
|                   <div slot="description" class="diagnostics-description"> | ||||
|                     Share crash reports and diagnostic information. | ||||
|                     <button | ||||
|                       class="link" | ||||
|                       title="Show more information about this" | ||||
|                       @click=${this._diagnosticsInformationDialog} | ||||
|                     > | ||||
|                       Learn more | ||||
|                     </button> | ||||
|                   </div> | ||||
|                   <ha-switch | ||||
|                     haptic | ||||
|                     .checked=${this.supervisor.supervisor.diagnostics} | ||||
|                     @change=${this._toggleDiagnostics} | ||||
|                   ></ha-switch> | ||||
|                 </ha-settings-row>` | ||||
|               : html`<div class="error"> | ||||
|                   You are running an unsupported installation. | ||||
|                   <button | ||||
|                     class="link" | ||||
|                     title="Learn more about how you can make your system compliant" | ||||
|                     @click=${this._unsupportedDialog} | ||||
|           <ha-settings-row> | ||||
|             <span slot="heading"> | ||||
|               Version | ||||
|             </span> | ||||
|             <span slot="description"> | ||||
|               ${this.supervisor.supervisor.version} | ||||
|             </span> | ||||
|           </ha-settings-row> | ||||
|           <ha-settings-row> | ||||
|             <span slot="heading"> | ||||
|               Newest Version | ||||
|             </span> | ||||
|             <span slot="description"> | ||||
|               ${this.supervisor.supervisor.version_latest} | ||||
|             </span> | ||||
|             ${this.supervisor.supervisor.update_available | ||||
|               ? html` | ||||
|                   <ha-progress-button | ||||
|                     title="Update the supervisor" | ||||
|                     @click=${this._supervisorUpdate} | ||||
|                   > | ||||
|                     Learn more | ||||
|                   </button> | ||||
|                 </div>`} | ||||
|             ${!this.supervisor.supervisor.healthy | ||||
|               ? html`<div class="error"> | ||||
|                   Your installation is running in an unhealthy state. | ||||
|                   <button | ||||
|                     class="link" | ||||
|                     title="Learn more about why your system is marked as unhealthy" | ||||
|                     @click=${this._unhealthyDialog} | ||||
|                   > | ||||
|                     Learn more | ||||
|                   </button> | ||||
|                 </div>` | ||||
|               : ""} | ||||
|           </div> | ||||
|           <div class="metrics-block"> | ||||
|             ${metrics.map( | ||||
|               (metric) => | ||||
|                 html` | ||||
|                   <supervisor-metric | ||||
|                     .description=${metric.description} | ||||
|                     .value=${metric.value ?? 0} | ||||
|                     .tooltip=${metric.tooltip} | ||||
|                   ></supervisor-metric> | ||||
|                     Update | ||||
|                   </ha-progress-button> | ||||
|                 ` | ||||
|             )} | ||||
|           </div> | ||||
|               : ""} | ||||
|           </ha-settings-row> | ||||
|           <ha-settings-row> | ||||
|             <span slot="heading"> | ||||
|               Channel | ||||
|             </span> | ||||
|             <span slot="description"> | ||||
|               ${this.supervisor.supervisor.channel} | ||||
|             </span> | ||||
|             ${this.supervisor.supervisor.channel === "beta" | ||||
|               ? html` | ||||
|                   <ha-progress-button | ||||
|                     @click=${this._toggleBeta} | ||||
|                     title="Get stable updates for Home Assistant, supervisor and host" | ||||
|                   > | ||||
|                     Leave beta channel | ||||
|                   </ha-progress-button> | ||||
|                 ` | ||||
|               : this.supervisor.supervisor.channel === "stable" | ||||
|               ? html` | ||||
|                   <ha-progress-button | ||||
|                     @click=${this._toggleBeta} | ||||
|                     title="Get beta updates for Home Assistant (RCs), supervisor and host" | ||||
|                   > | ||||
|                     Join beta channel | ||||
|                   </ha-progress-button> | ||||
|                 ` | ||||
|               : ""} | ||||
|           </ha-settings-row> | ||||
|  | ||||
|           ${this.supervisor.supervisor.supported | ||||
|             ? html` <ha-settings-row three-line> | ||||
|                 <span slot="heading"> | ||||
|                   Share Diagnostics | ||||
|                 </span> | ||||
|                 <div slot="description" class="diagnostics-description"> | ||||
|                   Share crash reports and diagnostic information. | ||||
|                   <button | ||||
|                     class="link" | ||||
|                     title="Show more information about this" | ||||
|                     @click=${this._diagnosticsInformationDialog} | ||||
|                   > | ||||
|                     Learn more | ||||
|                   </button> | ||||
|                 </div> | ||||
|                 <ha-switch | ||||
|                   haptic | ||||
|                   .checked=${this.supervisor.supervisor.diagnostics} | ||||
|                   @change=${this._toggleDiagnostics} | ||||
|                 ></ha-switch> | ||||
|               </ha-settings-row>` | ||||
|             : html`<div class="error"> | ||||
|                 You are running an unsupported installation. | ||||
|                 <button | ||||
|                   class="link" | ||||
|                   title="Learn more about how you can make your system compliant" | ||||
|                   @click=${this._unsupportedDialog} | ||||
|                 > | ||||
|                   Learn more | ||||
|                 </button> | ||||
|               </div>`} | ||||
|           ${!this.supervisor.supervisor.healthy | ||||
|             ? html`<div class="error"> | ||||
|                 Your installtion is running in an unhealthy state. | ||||
|                 <button | ||||
|                   class="link" | ||||
|                   title="Learn more about why your system is marked as unhealthy" | ||||
|                   @click=${this._unhealthyDialog} | ||||
|                 > | ||||
|                   Learn more | ||||
|                 </button> | ||||
|               </div>` | ||||
|             : ""} | ||||
|         </div> | ||||
|         <div class="card-actions"> | ||||
|           <ha-progress-button | ||||
|             @click=${this._supervisorReload} | ||||
|             title="Reload parts of the Supervisor" | ||||
|           > | ||||
|             Reload Supervisor | ||||
|             Reload | ||||
|           </ha-progress-button> | ||||
|           <ha-progress-button | ||||
|             class="warning" | ||||
|             @click=${this._supervisorRestart} | ||||
|             title="Restart the Supervisor" | ||||
|           > | ||||
|             Restart Supervisor | ||||
|             Restart | ||||
|           </ha-progress-button> | ||||
|         </div> | ||||
|       </ha-card> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(): void { | ||||
|     this._loadData(); | ||||
|   } | ||||
|  | ||||
|   private async _loadData(): Promise<void> { | ||||
|     this._metrics = await fetchHassioStats(this.hass, "supervisor"); | ||||
|   } | ||||
|  | ||||
|   private async _toggleBeta(ev: CustomEvent): Promise<void> { | ||||
|     const button = ev.currentTarget as any; | ||||
|     button.progress = true; | ||||
| @@ -317,25 +274,14 @@ class HassioSupervisorInfo extends LitElement { | ||||
|  | ||||
|   private async _reloadSupervisor(): Promise<void> { | ||||
|     await reloadSupervisor(this.hass); | ||||
|     fireEvent(this, "supervisor-store-refresh", { store: "supervisor" }); | ||||
|     const supervisor = await fetchHassioSupervisorInfo(this.hass); | ||||
|     fireEvent(this, "supervisor-update", { supervisor }); | ||||
|   } | ||||
|  | ||||
|   private async _supervisorRestart(ev: CustomEvent): Promise<void> { | ||||
|     const button = ev.currentTarget as any; | ||||
|     button.progress = true; | ||||
|  | ||||
|     const confirmed = await showConfirmationDialog(this, { | ||||
|       title: "Restart the Supervisor", | ||||
|       text: "Are you sure you want to restart the Supervisor", | ||||
|       confirmText: "restart", | ||||
|       dismissText: "cancel", | ||||
|     }); | ||||
|  | ||||
|     if (!confirmed) { | ||||
|       button.progress = false; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|       await restartSupervisor(this.hass); | ||||
|     } catch (err) { | ||||
| @@ -366,7 +312,6 @@ class HassioSupervisorInfo extends LitElement { | ||||
|  | ||||
|     try { | ||||
|       await updateSupervisor(this.hass); | ||||
|       fireEvent(this, "supervisor-store-refresh", { store: "supervisor" }); | ||||
|     } catch (err) { | ||||
|       showAlertDialog(this, { | ||||
|         title: "Failed to update the supervisor", | ||||
| @@ -481,15 +426,6 @@ class HassioSupervisorInfo extends LitElement { | ||||
|           justify-content: space-between; | ||||
|           align-items: center; | ||||
|         } | ||||
|         .card-content { | ||||
|           display: flex; | ||||
|           flex-direction: column; | ||||
|           height: calc(100% - 124px); | ||||
|           justify-content: space-between; | ||||
|         } | ||||
|         .metrics-block { | ||||
|           margin-top: 16px; | ||||
|         } | ||||
|         button.link { | ||||
|           color: var(--primary-color); | ||||
|         } | ||||
|   | ||||
							
								
								
									
										185
									
								
								hassio/src/system/hassio-system-metrics.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								hassio/src/system/hassio-system-metrics.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,185 @@ | ||||
| import "@material/mwc-button"; | ||||
| import "@material/mwc-list/mwc-list-item"; | ||||
| import { | ||||
|   css, | ||||
|   CSSResult, | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
|   LitElement, | ||||
|   property, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { classMap } from "lit-html/directives/class-map"; | ||||
| import memoizeOne from "memoize-one"; | ||||
| import "../../../src/components/buttons/ha-progress-button"; | ||||
| import "../../../src/components/ha-bar"; | ||||
| import "../../../src/components/ha-button-menu"; | ||||
| import "../../../src/components/ha-card"; | ||||
| import "../../../src/components/ha-settings-row"; | ||||
| import { fetchHassioStats, HassioStats } from "../../../src/data/hassio/common"; | ||||
| import { HassioHostInfo } from "../../../src/data/hassio/host"; | ||||
| import { Supervisor } from "../../../src/data/supervisor/supervisor"; | ||||
| import { haStyle } from "../../../src/resources/styles"; | ||||
| import { HomeAssistant } from "../../../src/types"; | ||||
| import { bytesToString } from "../../../src/util/bytes-to-string"; | ||||
| import { | ||||
|   getValueInPercentage, | ||||
|   roundWithOneDecimal, | ||||
| } from "../../../src/util/calculate"; | ||||
| import { hassioStyle } from "../resources/hassio-style"; | ||||
|  | ||||
| @customElement("hassio-system-metrics") | ||||
| class HassioSystemMetrics extends LitElement { | ||||
|   @property({ attribute: false }) public hass!: HomeAssistant; | ||||
|  | ||||
|   @property({ attribute: false }) public supervisor!: Supervisor; | ||||
|  | ||||
|   @internalProperty() private _supervisorMetrics?: HassioStats; | ||||
|  | ||||
|   @internalProperty() private _coreMetrics?: HassioStats; | ||||
|  | ||||
|   protected render(): TemplateResult | void { | ||||
|     const metrics = [ | ||||
|       { | ||||
|         description: "Core CPU Usage", | ||||
|         value: this._coreMetrics?.cpu_percent, | ||||
|       }, | ||||
|       { | ||||
|         description: "Core RAM Usage", | ||||
|         value: this._coreMetrics?.memory_percent, | ||||
|         tooltip: `${bytesToString( | ||||
|           this._coreMetrics?.memory_usage | ||||
|         )}/${bytesToString(this._coreMetrics?.memory_limit)}`, | ||||
|       }, | ||||
|       { | ||||
|         description: "Supervisor CPU Usage", | ||||
|         value: this._supervisorMetrics?.cpu_percent, | ||||
|       }, | ||||
|       { | ||||
|         description: "Supervisor RAM Usage", | ||||
|         value: this._supervisorMetrics?.memory_percent, | ||||
|         tooltip: `${bytesToString( | ||||
|           this._supervisorMetrics?.memory_usage | ||||
|         )}/${bytesToString(this._supervisorMetrics?.memory_limit)}`, | ||||
|       }, | ||||
|       { | ||||
|         description: "Used Space", | ||||
|         value: this._getUsedSpace(this.supervisor.host), | ||||
|         tooltip: `${this.supervisor.host.disk_used} GB/${this.supervisor.host.disk_total} GB`, | ||||
|       }, | ||||
|     ]; | ||||
|  | ||||
|     return html` | ||||
|       <ha-card header="System Metrics"> | ||||
|         <div class="card-content"> | ||||
|           ${metrics.map((metric) => | ||||
|             this._renderMetric( | ||||
|               metric.description, | ||||
|               metric.value ?? 0, | ||||
|               metric.tooltip | ||||
|             ) | ||||
|           )} | ||||
|         </div> | ||||
|       </ha-card> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   protected firstUpdated(): void { | ||||
|     this._loadData(); | ||||
|   } | ||||
|  | ||||
|   private _renderMetric( | ||||
|     description: string, | ||||
|     value: number, | ||||
|     tooltip?: string | ||||
|   ): TemplateResult { | ||||
|     const roundedValue = roundWithOneDecimal(value); | ||||
|     return html`<ha-settings-row> | ||||
|       <span slot="heading"> | ||||
|         ${description} | ||||
|       </span> | ||||
|       <div slot="description" title="${tooltip ?? ""}"> | ||||
|         <span class="value"> | ||||
|           ${roundedValue}% | ||||
|         </span> | ||||
|         <ha-bar | ||||
|           class="${classMap({ | ||||
|             "target-warning": roundedValue > 50, | ||||
|             "target-critical": roundedValue > 85, | ||||
|           })}" | ||||
|           .value=${value} | ||||
|         ></ha-bar> | ||||
|       </div> | ||||
|     </ha-settings-row>`; | ||||
|   } | ||||
|  | ||||
|   private _getUsedSpace = memoizeOne((hostInfo: HassioHostInfo) => | ||||
|     roundWithOneDecimal( | ||||
|       getValueInPercentage(hostInfo.disk_used, 0, hostInfo.disk_total) | ||||
|     ) | ||||
|   ); | ||||
|  | ||||
|   private async _loadData(): Promise<void> { | ||||
|     const [supervisor, core] = await Promise.all([ | ||||
|       fetchHassioStats(this.hass, "supervisor"), | ||||
|       fetchHassioStats(this.hass, "core"), | ||||
|     ]); | ||||
|     this._supervisorMetrics = supervisor; | ||||
|     this._coreMetrics = core; | ||||
|   } | ||||
|  | ||||
|   static get styles(): CSSResult[] { | ||||
|     return [ | ||||
|       haStyle, | ||||
|       hassioStyle, | ||||
|       css` | ||||
|         ha-card { | ||||
|           height: 100%; | ||||
|           justify-content: space-between; | ||||
|           flex-direction: column; | ||||
|           display: flex; | ||||
|         } | ||||
|         ha-settings-row { | ||||
|           padding: 0; | ||||
|           height: 54px; | ||||
|           width: 100%; | ||||
|         } | ||||
|         ha-settings-row > div[slot="description"] { | ||||
|           white-space: normal; | ||||
|           color: var(--secondary-text-color); | ||||
|           display: flex; | ||||
|           justify-content: space-between; | ||||
|         } | ||||
|         ha-bar { | ||||
|           --ha-bar-primary-color: var( | ||||
|             --hassio-bar-ok-color, | ||||
|             var(--success-color) | ||||
|           ); | ||||
|         } | ||||
|         .target-warning { | ||||
|           --ha-bar-primary-color: var( | ||||
|             --hassio-bar-warning-color, | ||||
|             var(--warning-color) | ||||
|           ); | ||||
|         } | ||||
|         .target-critical { | ||||
|           --ha-bar-primary-color: var( | ||||
|             --hassio-bar-critical-color, | ||||
|             var(--error-color) | ||||
|           ); | ||||
|         } | ||||
|         .value { | ||||
|           width: 42px; | ||||
|           padding-right: 4px; | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   } | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   interface HTMLElementTagNameMap { | ||||
|     "hassio-system-metrics": HassioSystemMetrics; | ||||
|   } | ||||
| } | ||||
| @@ -13,10 +13,10 @@ import { haStyle } from "../../../src/resources/styles"; | ||||
| import { HomeAssistant, Route } from "../../../src/types"; | ||||
| import { supervisorTabs } from "../hassio-tabs"; | ||||
| import { hassioStyle } from "../resources/hassio-style"; | ||||
| import "./hassio-core-info"; | ||||
| import "./hassio-host-info"; | ||||
| import "./hassio-supervisor-info"; | ||||
| import "./hassio-supervisor-log"; | ||||
| import "./hassio-system-metrics"; | ||||
|  | ||||
| @customElement("hassio-system") | ||||
| class HassioSystem extends LitElement { | ||||
| @@ -41,10 +41,6 @@ class HassioSystem extends LitElement { | ||||
|         <span slot="header">System</span> | ||||
|         <div class="content"> | ||||
|           <div class="card-group"> | ||||
|             <hassio-core-info | ||||
|               .hass=${this.hass} | ||||
|               .supervisor=${this.supervisor} | ||||
|             ></hassio-core-info> | ||||
|             <hassio-supervisor-info | ||||
|               .hass=${this.hass} | ||||
|               .supervisor=${this.supervisor} | ||||
| @@ -53,6 +49,10 @@ class HassioSystem extends LitElement { | ||||
|               .hass=${this.hass} | ||||
|               .supervisor=${this.supervisor} | ||||
|             ></hassio-host-info> | ||||
|             <hassio-system-metrics | ||||
|               .hass=${this.hass} | ||||
|               .supervisor=${this.supervisor} | ||||
|             ></hassio-system-metrics> | ||||
|           </div> | ||||
|           <hassio-supervisor-log .hass=${this.hass}></hassio-supervisor-log> | ||||
|         </div> | ||||
|   | ||||
| @@ -1,7 +0,0 @@ | ||||
| import memoizeOne from "memoize-one"; | ||||
| import { SupervisorArch } from "../../../src/data/supervisor/supervisor"; | ||||
|  | ||||
| export const addonArchIsSupported = memoizeOne( | ||||
|   (supported_archs: SupervisorArch[], addon_archs: SupervisorArch[]) => | ||||
|     addon_archs.some((arch) => supported_archs.includes(arch)) | ||||
| ); | ||||
							
								
								
									
										41
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								package.json
									
									
									
									
									
								
							| @@ -22,7 +22,6 @@ | ||||
|   "author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)", | ||||
|   "license": "Apache-2.0", | ||||
|   "dependencies": { | ||||
|     "@braintree/sanitize-url": "^5.0.0", | ||||
|     "@formatjs/intl-getcanonicallocales": "^1.4.6", | ||||
|     "@formatjs/intl-pluralrules": "^3.4.10", | ||||
|     "@fullcalendar/common": "5.1.0", | ||||
| @@ -30,22 +29,22 @@ | ||||
|     "@fullcalendar/daygrid": "5.1.0", | ||||
|     "@fullcalendar/interaction": "5.1.0", | ||||
|     "@fullcalendar/list": "5.1.0", | ||||
|     "@material/chips": "=9.0.0-canary.1c156d69d.0", | ||||
|     "@material/mwc-button": "^0.20.0", | ||||
|     "@material/mwc-checkbox": "^0.20.0", | ||||
|     "@material/mwc-circular-progress": "^0.20.0", | ||||
|     "@material/mwc-dialog": "^0.20.0", | ||||
|     "@material/mwc-fab": "^0.20.0", | ||||
|     "@material/mwc-formfield": "^0.20.0", | ||||
|     "@material/mwc-icon-button": "^0.20.0", | ||||
|     "@material/mwc-list": "^0.20.0", | ||||
|     "@material/mwc-menu": "^0.20.0", | ||||
|     "@material/mwc-radio": "^0.20.0", | ||||
|     "@material/mwc-ripple": "^0.20.0", | ||||
|     "@material/mwc-switch": "^0.20.0", | ||||
|     "@material/mwc-tab": "^0.20.0", | ||||
|     "@material/mwc-tab-bar": "^0.20.0", | ||||
|     "@material/top-app-bar": "=9.0.0-canary.1c156d69d.0", | ||||
|     "@material/chips": "=8.0.0-canary.774dcfc8e.0", | ||||
|     "@material/mwc-button": "^0.19.0", | ||||
|     "@material/mwc-checkbox": "^0.19.0", | ||||
|     "@material/mwc-circular-progress": "^0.19.0", | ||||
|     "@material/mwc-dialog": "^0.19.0", | ||||
|     "@material/mwc-fab": "^0.19.0", | ||||
|     "@material/mwc-formfield": "^0.19.0", | ||||
|     "@material/mwc-icon-button": "^0.19.0", | ||||
|     "@material/mwc-list": "^0.19.0", | ||||
|     "@material/mwc-menu": "^0.19.0", | ||||
|     "@material/mwc-radio": "^0.19.0", | ||||
|     "@material/mwc-ripple": "^0.19.0", | ||||
|     "@material/mwc-switch": "^0.19.0", | ||||
|     "@material/mwc-tab": "^0.19.0", | ||||
|     "@material/mwc-tab-bar": "^0.19.0", | ||||
|     "@material/top-app-bar": "=8.0.0-canary.774dcfc8e.0", | ||||
|     "@mdi/js": "5.6.55", | ||||
|     "@mdi/svg": "5.6.55", | ||||
|     "@polymer/app-layout": "^3.0.2", | ||||
| @@ -101,7 +100,7 @@ | ||||
|     "fuse.js": "^6.0.0", | ||||
|     "google-timezones-json": "^1.0.2", | ||||
|     "hls.js": "^0.13.2", | ||||
|     "home-assistant-js-websocket": "^5.8.1", | ||||
|     "home-assistant-js-websocket": "^5.4.1", | ||||
|     "idb-keyval": "^3.2.0", | ||||
|     "intl-messageformat": "^8.3.9", | ||||
|     "js-yaml": "^3.13.1", | ||||
| @@ -110,7 +109,7 @@ | ||||
|     "lit-element": "^2.4.0", | ||||
|     "lit-html": "^1.3.0", | ||||
|     "lit-virtualizer": "^0.4.2", | ||||
|     "marked": "2.0.0", | ||||
|     "marked": "^1.1.1", | ||||
|     "mdn-polyfills": "^5.16.0", | ||||
|     "memoize-one": "^5.0.2", | ||||
|     "node-vibrant": "3.2.1-alpha.1", | ||||
| @@ -121,7 +120,7 @@ | ||||
|     "resize-observer-polyfill": "^1.5.1", | ||||
|     "roboto-fontface": "^0.10.0", | ||||
|     "sortablejs": "^1.10.2", | ||||
|     "superstruct": "^0.10.13", | ||||
|     "superstruct": "^0.10.12", | ||||
|     "tinykeys": "^1.1.1", | ||||
|     "unfetch": "^4.1.0", | ||||
|     "vis-data": "^7.1.1", | ||||
| @@ -161,7 +160,7 @@ | ||||
|     "@types/js-yaml": "^3.12.1", | ||||
|     "@types/leaflet": "^1.4.3", | ||||
|     "@types/leaflet-draw": "^1.0.1", | ||||
|     "@types/marked": "^1.2.2", | ||||
|     "@types/marked": "^1.1.0", | ||||
|     "@types/memoize-one": "4.1.0", | ||||
|     "@types/mocha": "^7.0.2", | ||||
|     "@types/resize-observer-browser": "^0.1.3", | ||||
|   | ||||
							
								
								
									
										2
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								setup.py
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ from setuptools import setup, find_packages | ||||
|  | ||||
| setup( | ||||
|     name="home-assistant-frontend", | ||||
|     version="20210208.0", | ||||
|     version="20201126.0", | ||||
|     description="The Home Assistant frontend", | ||||
|     url="https://github.com/home-assistant/home-assistant-polymer", | ||||
|     author="The Home Assistant Authors", | ||||
|   | ||||
| @@ -3,9 +3,9 @@ import { | ||||
|   css, | ||||
|   CSSResult, | ||||
|   html, | ||||
|   internalProperty, | ||||
|   LitElement, | ||||
|   property, | ||||
|   internalProperty, | ||||
|   PropertyValues, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
|   | ||||
| @@ -2,25 +2,21 @@ import { | ||||
|   css, | ||||
|   CSSResult, | ||||
|   html, | ||||
|   internalProperty, | ||||
|   LitElement, | ||||
|   property, | ||||
|   internalProperty, | ||||
|   PropertyValues, | ||||
| } from "lit-element"; | ||||
| import punycode from "punycode"; | ||||
| import { extractSearchParamsObject } from "../common/url/search-params"; | ||||
| import { | ||||
|   AuthProvider, | ||||
|   AuthUrlSearchParams, | ||||
|   fetchAuthProviders, | ||||
|   AuthUrlSearchParams, | ||||
| } from "../data/auth"; | ||||
| import { | ||||
|   DiscoveryInformation, | ||||
|   fetchDiscoveryInformation, | ||||
| } from "../data/discovery"; | ||||
| import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin"; | ||||
| import { registerServiceWorker } from "../util/register-service-worker"; | ||||
| import "./ha-auth-flow"; | ||||
| import { extractSearchParamsObject } from "../common/url/search-params"; | ||||
| import punycode from "punycode"; | ||||
|  | ||||
| import("./ha-pick-auth-provider"); | ||||
|  | ||||
| @@ -35,8 +31,6 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) { | ||||
|  | ||||
|   @internalProperty() private _authProviders?: AuthProvider[]; | ||||
|  | ||||
|   @internalProperty() private _discovery?: DiscoveryInformation; | ||||
|  | ||||
|   constructor() { | ||||
|     super(); | ||||
|     this.translationFragment = "page-authorize"; | ||||
| @@ -64,17 +58,14 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) { | ||||
|     // the name with a bold tag. | ||||
|     const loggingInWith = document.createElement("div"); | ||||
|     loggingInWith.innerText = this.localize( | ||||
|       this._discovery?.location_name | ||||
|         ? "ui.panel.page-authorize.logging_in_to_with" | ||||
|         : "ui.panel.page-authorize.logging_in_with", | ||||
|       "locationName", | ||||
|       "LOCATION", | ||||
|       "ui.panel.page-authorize.logging_in_with", | ||||
|       "authProviderName", | ||||
|       "NAME" | ||||
|     ); | ||||
|     loggingInWith.innerHTML = loggingInWith.innerHTML | ||||
|       .replace("**LOCATION**", `<b>${this._discovery?.location_name}</b>`) | ||||
|       .replace("**NAME**", `<b>${this._authProvider!.name}</b>`); | ||||
|     loggingInWith.innerHTML = loggingInWith.innerHTML.replace( | ||||
|       "**NAME**", | ||||
|       `<b>${this._authProvider!.name}</b>` | ||||
|     ); | ||||
|  | ||||
|     const inactiveProviders = this._authProviders.filter( | ||||
|       (prv) => prv !== this._authProvider | ||||
| @@ -114,7 +105,6 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) { | ||||
|   protected firstUpdated(changedProps: PropertyValues) { | ||||
|     super.firstUpdated(changedProps); | ||||
|     this._fetchAuthProviders(); | ||||
|     this._fetchDiscoveryInfo(); | ||||
|  | ||||
|     if (!this.redirectUri) { | ||||
|       return; | ||||
| @@ -136,10 +126,6 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private async _fetchDiscoveryInfo() { | ||||
|     this._discovery = await fetchDiscoveryInformation(); | ||||
|   } | ||||
|  | ||||
|   private async _fetchAuthProviders() { | ||||
|     // Fetch auth providers | ||||
|     try { | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { isComponentLoaded } from "./is_component_loaded"; | ||||
| import { PageNavigation } from "../../layouts/hass-tabs-subpage"; | ||||
| import { HomeAssistant } from "../../types"; | ||||
| import { isComponentLoaded } from "./is_component_loaded"; | ||||
|  | ||||
| export const canShowPage = (hass: HomeAssistant, page: PageNavigation) => { | ||||
|   return ( | ||||
|   | ||||
| @@ -4,4 +4,4 @@ import { HomeAssistant } from "../../types"; | ||||
| export const isComponentLoaded = ( | ||||
|   hass: HomeAssistant, | ||||
|   component: string | ||||
| ): boolean => hass && hass.config.components.includes(component); | ||||
| ): boolean => hass && hass.config.components.indexOf(component) !== -1; | ||||
|   | ||||
| @@ -1,15 +1,11 @@ | ||||
| export const atLeastVersion = ( | ||||
|   version: string, | ||||
|   major: number, | ||||
|   minor: number, | ||||
|   patch?: number | ||||
|   minor: number | ||||
| ): boolean => { | ||||
|   const [haMajor, haMinor, haPatch] = version.split(".", 3); | ||||
|   const [haMajor, haMinor] = version.split(".", 2); | ||||
|   return ( | ||||
|     Number(haMajor) > major || | ||||
|     (Number(haMajor) === major && Number(haMinor) >= minor) || | ||||
|     (patch !== undefined && | ||||
|       Number(haMajor) === major && Number(haMinor) === minor && | ||||
|       Number(haPatch) >= patch) | ||||
|     (Number(haMajor) === major && Number(haMinor) >= minor) | ||||
|   ); | ||||
| }; | ||||
|   | ||||
| @@ -34,7 +34,6 @@ export const FIXED_DOMAIN_ICONS = { | ||||
|   light: "hass:lightbulb", | ||||
|   mailbox: "hass:mailbox", | ||||
|   notify: "hass:comment-alert", | ||||
|   number: "hass:ray-vertex", | ||||
|   persistent_notification: "hass:bell", | ||||
|   person: "hass:account", | ||||
|   plant: "hass:flower", | ||||
| @@ -78,7 +77,6 @@ export const DOMAINS_WITH_CARD = [ | ||||
|   "input_text", | ||||
|   "lock", | ||||
|   "media_player", | ||||
|   "number", | ||||
|   "scene", | ||||
|   "script", | ||||
|   "timer", | ||||
| @@ -116,7 +114,6 @@ export const DOMAINS_HIDE_MORE_INFO = [ | ||||
|   "input_number", | ||||
|   "input_select", | ||||
|   "input_text", | ||||
|   "number", | ||||
|   "scene", | ||||
| ]; | ||||
|  | ||||
| @@ -141,9 +138,6 @@ export const DOMAINS_TOGGLE = new Set([ | ||||
|   "humidifier", | ||||
| ]); | ||||
|  | ||||
| /** Domains that have a dynamic entity image / picture. */ | ||||
| export const DOMAINS_WITH_DYNAMIC_PICTURE = new Set(["camera", "media_player"]); | ||||
|  | ||||
| /** Temperature units. */ | ||||
| export const UNIT_C = "°C"; | ||||
| export const UNIT_F = "°F"; | ||||
|   | ||||
| @@ -1,7 +0,0 @@ | ||||
| export default function checkValidDate(date?: Date): boolean { | ||||
|   if (!date) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   return date instanceof Date && !isNaN(date.valueOf()); | ||||
| } | ||||
| @@ -9,12 +9,3 @@ export const formatDate = toLocaleDateStringSupportsOptions | ||||
|         day: "numeric", | ||||
|       }) | ||||
|   : (dateObj: Date) => format(dateObj, "longDate"); | ||||
|  | ||||
| export const formatDateWeekday = toLocaleDateStringSupportsOptions | ||||
|   ? (dateObj: Date, locales: string) => | ||||
|       dateObj.toLocaleDateString(locales, { | ||||
|         weekday: "long", | ||||
|         month: "short", | ||||
|         day: "numeric", | ||||
|       }) | ||||
|   : (dateObj: Date) => format(dateObj, "dddd, MMM D"); | ||||
|   | ||||
| @@ -17,12 +17,3 @@ export const formatTimeWithSeconds = toLocaleTimeStringSupportsOptions | ||||
|         second: "2-digit", | ||||
|       }) | ||||
|   : (dateObj: Date) => format(dateObj, "mediumTime"); | ||||
|  | ||||
| export const formatTimeWeekday = toLocaleTimeStringSupportsOptions | ||||
|   ? (dateObj: Date, locales: string) => | ||||
|       dateObj.toLocaleTimeString(locales, { | ||||
|         weekday: "long", | ||||
|         hour: "numeric", | ||||
|         minute: "2-digit", | ||||
|       }) | ||||
|   : (dateObj: Date) => format(dateObj, "dddd, HH:mm"); | ||||
|   | ||||
| @@ -1,6 +0,0 @@ | ||||
| export const ensureArray = (value?: any) => { | ||||
|   if (!value || Array.isArray(value)) { | ||||
|     return value; | ||||
|   } | ||||
|   return [value]; | ||||
| }; | ||||
| @@ -3,9 +3,9 @@ import { UNAVAILABLE, UNKNOWN } from "../../data/entity"; | ||||
| import { formatDate } from "../datetime/format_date"; | ||||
| import { formatDateTime } from "../datetime/format_date_time"; | ||||
| import { formatTime } from "../datetime/format_time"; | ||||
| import { formatNumber } from "../string/format_number"; | ||||
| import { LocalizeFunc } from "../translations/localize"; | ||||
| import { computeStateDomain } from "./compute_state_domain"; | ||||
| import { formatNumber } from "../string/format_number"; | ||||
|  | ||||
| export const computeStateDisplay = ( | ||||
|   localize: LocalizeFunc, | ||||
| @@ -63,14 +63,10 @@ export const computeStateDisplay = ( | ||||
|  | ||||
|   if (domain === "humidifier") { | ||||
|     if (compareState === "on" && stateObj.attributes.humidity) { | ||||
|       return `${stateObj.attributes.humidity} %`; | ||||
|       return `${stateObj.attributes.humidity}%`; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (domain === "counter") { | ||||
|     return formatNumber(compareState, language); | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     // Return device class translation | ||||
|     (stateObj.attributes.device_class && | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import { HassEntity } from "home-assistant-js-websocket"; | ||||
| import { UNAVAILABLE } from "../../data/entity"; | ||||
| import { HomeAssistant } from "../../types"; | ||||
| import { DOMAINS_WITH_CARD } from "../const"; | ||||
| import { canToggleState } from "./can_toggle_state"; | ||||
| import { computeStateDomain } from "./compute_state_domain"; | ||||
| import { UNAVAILABLE } from "../../data/entity"; | ||||
|  | ||||
| export const stateCardType = (hass: HomeAssistant, stateObj: HassEntity) => { | ||||
|   if (stateObj.state === UNAVAILABLE) { | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| // We import the minified bundle because the unminified bundle | ||||
| // has some quirks that break wds. See #7784 for unminified version. | ||||
| import Vibrant from "node-vibrant/dist/vibrant"; | ||||
| import type { Swatch, Vec3 } from "@vibrant/color"; | ||||
| import Vibrant from "node-vibrant/lib/browser"; | ||||
| import MMCQ from "@vibrant/quantizer-mmcq"; | ||||
| import { BasicPipeline } from "@vibrant/core/lib/pipeline"; | ||||
| import { Swatch, Vec3 } from "@vibrant/color"; | ||||
| import { getRGBContrastRatio } from "../color/rgb"; | ||||
|  | ||||
| const CONTRAST_RATIO = 4.5; | ||||
| @@ -104,15 +104,23 @@ const customGenerator = (colors: Swatch[]) => { | ||||
|   } | ||||
|  | ||||
|   return { | ||||
|     // We can't import Swatch constructor from the minified bundle, take it from background color. | ||||
|     // @ts-expect-error | ||||
|     foreground: new backgroundColor.constructor(foregroundColor, 0), | ||||
|     foreground: new Swatch(foregroundColor, 0), | ||||
|     background: backgroundColor, | ||||
|   }; | ||||
| }; | ||||
|  | ||||
| // Set our custom generator as the default. | ||||
| Vibrant._pipeline.generator.register("default", customGenerator); | ||||
| Vibrant.use( | ||||
|   new BasicPipeline().filter | ||||
|     .register( | ||||
|       "default", | ||||
|       (r: number, g: number, b: number, a: number) => | ||||
|         a >= 125 && !(r > 250 && g > 250 && b > 250) | ||||
|     ) | ||||
|     .quantizer.register("mmcq", MMCQ) | ||||
|     // Our generator has different output | ||||
|     // @ts-expect-error | ||||
|     .generator.register("default", customGenerator) | ||||
| ); | ||||
|  | ||||
| export const extractColors = (url: string, downsampleColors = 16) => | ||||
|   new Vibrant(url, { | ||||
|   | ||||
| @@ -1,5 +1,3 @@ | ||||
| import "@material/mwc-icon-button/mwc-icon-button"; | ||||
| import { mdiClose, mdiMagnify } from "@mdi/js"; | ||||
| import "@polymer/paper-input/paper-input"; | ||||
| import { | ||||
|   css, | ||||
| @@ -12,6 +10,8 @@ import { html, TemplateResult } from "lit-html"; | ||||
| import { classMap } from "lit-html/directives/class-map"; | ||||
| import "../../components/ha-svg-icon"; | ||||
| import { fireEvent } from "../dom/fire_event"; | ||||
| import { mdiMagnify, mdiClose } from "@mdi/js"; | ||||
| import "@material/mwc-icon-button/mwc-icon-button"; | ||||
|  | ||||
| @customElement("search-input") | ||||
| class SearchInput extends LitElement { | ||||
|   | ||||
| @@ -1,45 +0,0 @@ | ||||
| import { StructError } from "superstruct"; | ||||
| import type { HomeAssistant } from "../../types"; | ||||
|  | ||||
| export const handleStructError = ( | ||||
|   hass: HomeAssistant, | ||||
|   err: Error | ||||
| ): { warnings: string[]; errors?: string[] } => { | ||||
|   if (!(err instanceof StructError)) { | ||||
|     return { warnings: [err.message], errors: undefined }; | ||||
|   } | ||||
|   const errors: string[] = []; | ||||
|   const warnings: string[] = []; | ||||
|   for (const failure of err.failures()) { | ||||
|     if (failure.value === undefined) { | ||||
|       errors.push( | ||||
|         hass.localize( | ||||
|           "ui.errors.config.key_missing", | ||||
|           "key", | ||||
|           failure.path.join(".") | ||||
|         ) | ||||
|       ); | ||||
|     } else if (failure.type === "never") { | ||||
|       warnings.push( | ||||
|         hass.localize( | ||||
|           "ui.errors.config.key_not_expected", | ||||
|           "key", | ||||
|           failure.path.join(".") | ||||
|         ) | ||||
|       ); | ||||
|     } else { | ||||
|       warnings.push( | ||||
|         hass.localize( | ||||
|           "ui.errors.config.key_wrong_type", | ||||
|           "key", | ||||
|           failure.path.join("."), | ||||
|           "type_correct", | ||||
|           failure.type, | ||||
|           "type_wrong", | ||||
|           JSON.stringify(failure.value) | ||||
|         ) | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|   return { warnings, errors }; | ||||
| }; | ||||
| @@ -1,30 +0,0 @@ | ||||
| import { struct, StructContext, StructResult } from "superstruct"; | ||||
|  | ||||
| const isEntityId = (value: unknown, context: StructContext): StructResult => { | ||||
|   if (typeof value !== "string") { | ||||
|     return [context.fail({ type: "string" })]; | ||||
|   } | ||||
|   if (!value.includes(".")) { | ||||
|     return [ | ||||
|       context.fail({ | ||||
|         type: "Entity ID should be in the format 'domain.entity'", | ||||
|       }), | ||||
|     ]; | ||||
|   } | ||||
|   return true; | ||||
| }; | ||||
|  | ||||
| export const EntityId = struct("entity-id", isEntityId); | ||||
|  | ||||
| const isEntityIdOrAll = ( | ||||
|   value: unknown, | ||||
|   context: StructContext | ||||
| ): StructResult => { | ||||
|   if (typeof value === "string" && value === "all") { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   return isEntityId(value, context); | ||||
| }; | ||||
|  | ||||
| export const EntityIdOrAll = struct("entity-id-all", isEntityIdOrAll); | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { shouldPolyfill } from "@formatjs/intl-pluralrules/should-polyfill"; | ||||
| import IntlMessageFormat from "intl-messageformat"; | ||||
| import { shouldPolyfill } from "@formatjs/intl-pluralrules/should-polyfill"; | ||||
| import { Resources } from "../../types"; | ||||
|  | ||||
| export type LocalizeFunc = (key: string, ...args: any[]) => string; | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user