mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-11 10:19:25 +00:00
Compare commits
2 Commits
bump-app-d
...
multiple-a
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e87accdfb4 | ||
![]() |
4dcfe3031d |
@@ -1,5 +1,5 @@
|
||||
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.148.1/containers/python-3/.devcontainer/base.Dockerfile
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/python:0-3.10
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/python:0-3.9
|
||||
|
||||
ENV \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
|
@@ -4,40 +4,28 @@
|
||||
"dockerfile": "Dockerfile",
|
||||
"context": ".."
|
||||
},
|
||||
"appPort": "8124:8123",
|
||||
"appPort": 8123,
|
||||
"context": "..",
|
||||
"postCreateCommand": "script/bootstrap",
|
||||
"containerEnv": {
|
||||
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}"
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"runem.lit-plugin",
|
||||
"github.vscode-pull-request-github",
|
||||
"eamodio.gitlens"
|
||||
],
|
||||
"settings": {
|
||||
"files.eol": "\n",
|
||||
"editor.tabSize": 2,
|
||||
"editor.formatOnPaste": false,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnType": true,
|
||||
"editor.renderWhitespace": "boundary",
|
||||
"editor.rulers": [80],
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"terminal.integrated.shell.linux": "/usr/bin/zsh",
|
||||
"gitlens.showWelcomeOnInstall": false,
|
||||
"gitlens.showWhatsNewAfterUpgrades": false,
|
||||
"workbench.startupEditor": "none"
|
||||
}
|
||||
}
|
||||
"extensions": [
|
||||
"github.vscode-pull-request-github",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"ms-vscode.vscode-typescript-tslint-plugin",
|
||||
"esbenp.prettier-vscode",
|
||||
"bierner.lit-html",
|
||||
"runem.lit-plugin",
|
||||
"ms-python.vscode-pylance"
|
||||
],
|
||||
"settings": {
|
||||
"terminal.integrated.shell.linux": "/bin/bash",
|
||||
"files.eol": "\n",
|
||||
"editor.tabSize": 2,
|
||||
"editor.formatOnPaste": false,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnType": true,
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"files.trimTrailingWhitespace": true
|
||||
}
|
||||
}
|
||||
|
137
.eslintrc.json
137
.eslintrc.json
@@ -1,12 +1,11 @@
|
||||
{
|
||||
"extends": [
|
||||
"airbnb-base",
|
||||
"airbnb-typescript/base",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:wc/recommended",
|
||||
"plugin:lit/all",
|
||||
"plugin:lit-a11y/recommended",
|
||||
"prettier"
|
||||
"plugin:lit/recommended",
|
||||
"prettier",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
@@ -30,101 +29,63 @@
|
||||
"__BUILD__": false,
|
||||
"__VERSION__": false,
|
||||
"__STATIC_PATH__": false,
|
||||
"__SUPERVISOR__": false,
|
||||
"Polymer": true
|
||||
"Polymer": true,
|
||||
"webkitSpeechRecognition": false,
|
||||
"ResizeObserver": false
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true
|
||||
},
|
||||
"rules": {
|
||||
"class-methods-use-this": "off",
|
||||
"new-cap": "off",
|
||||
"prefer-template": "off",
|
||||
"object-shorthand": "off",
|
||||
"func-names": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"strict": "off",
|
||||
"no-plusplus": "off",
|
||||
"no-bitwise": "error",
|
||||
"comma-dangle": "off",
|
||||
"vars-on-top": "off",
|
||||
"no-continue": "off",
|
||||
"no-param-reassign": "off",
|
||||
"no-multi-assign": "off",
|
||||
"no-console": "error",
|
||||
"radix": "off",
|
||||
"no-alert": "off",
|
||||
"no-nested-ternary": "off",
|
||||
"prefer-destructuring": "off",
|
||||
"class-methods-use-this": 0,
|
||||
"new-cap": 0,
|
||||
"prefer-template": 0,
|
||||
"object-shorthand": 0,
|
||||
"func-names": 0,
|
||||
"prefer-arrow-callback": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"strict": 0,
|
||||
"prefer-spread": 0,
|
||||
"no-plusplus": 0,
|
||||
"no-bitwise": 2,
|
||||
"comma-dangle": 0,
|
||||
"vars-on-top": 0,
|
||||
"no-continue": 0,
|
||||
"no-param-reassign": 0,
|
||||
"no-multi-assign": 0,
|
||||
"no-console": 2,
|
||||
"radix": 0,
|
||||
"no-alert": 0,
|
||||
"no-return-await": 0,
|
||||
"no-nested-ternary": 0,
|
||||
"prefer-destructuring": 0,
|
||||
"no-restricted-globals": [2, "event"],
|
||||
"prefer-promise-reject-errors": "off",
|
||||
"import/prefer-default-export": "off",
|
||||
"import/no-default-export": "off",
|
||||
"import/no-unresolved": "off",
|
||||
"import/no-cycle": "off",
|
||||
"prefer-promise-reject-errors": 0,
|
||||
"import/order": 0,
|
||||
"import/prefer-default-export": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/no-cycle": 0,
|
||||
"import/extensions": [
|
||||
"error",
|
||||
2,
|
||||
"ignorePackages",
|
||||
{
|
||||
"ts": "never",
|
||||
"js": "never"
|
||||
}
|
||||
{ "ts": "never", "js": "never" }
|
||||
],
|
||||
"no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"],
|
||||
"object-curly-newline": "off",
|
||||
"default-case": "off",
|
||||
"wc/no-self-class": "off",
|
||||
"no-shadow": "off",
|
||||
"@typescript-eslint/camelcase": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-shadow": ["error"],
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"off",
|
||||
{
|
||||
"selector": "default",
|
||||
"format": ["camelCase", "snake_case"],
|
||||
"leadingUnderscore": "allow",
|
||||
"trailingUnderscore": "allow"
|
||||
},
|
||||
{
|
||||
"selector": ["variable"],
|
||||
"format": ["camelCase", "snake_case", "UPPER_CASE"],
|
||||
"leadingUnderscore": "allow",
|
||||
"trailingUnderscore": "allow"
|
||||
},
|
||||
{
|
||||
"selector": "typeLike",
|
||||
"format": ["PascalCase"]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"unused-imports/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_",
|
||||
"ignoreRestSiblings": true
|
||||
}
|
||||
],
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"lit/attribute-value-entities": "off",
|
||||
"lit/no-template-map": "off",
|
||||
"lit/no-native-attributes": "warn",
|
||||
"lit/no-this-assign-in-render": "warn",
|
||||
"lit-a11y/click-events-have-key-events": ["off"],
|
||||
"lit-a11y/no-autofocus": "off",
|
||||
"lit-a11y/alt-text": "warn",
|
||||
"lit-a11y/anchor-is-valid": "warn",
|
||||
"lit-a11y/role-has-required-aria-attrs": "warn"
|
||||
"object-curly-newline": 0,
|
||||
"default-case": 0,
|
||||
"wc/no-self-class": 0,
|
||||
"no-shadow": 0,
|
||||
"@typescript-eslint/camelcase": 0,
|
||||
"@typescript-eslint/ban-ts-comment": 0,
|
||||
"@typescript-eslint/no-use-before-define": 0,
|
||||
"@typescript-eslint/no-non-null-assertion": 0,
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/no-unused-vars": 0,
|
||||
"@typescript-eslint/explicit-function-return-type": 0,
|
||||
"@typescript-eslint/explicit-module-boundary-types": 0,
|
||||
"@typescript-eslint/no-shadow": ["error"]
|
||||
},
|
||||
"plugins": ["disable", "unused-imports"],
|
||||
"plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],
|
||||
"processor": "disable/disable"
|
||||
}
|
||||
|
@@ -51,7 +51,7 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
|
||||
<!--
|
||||
Provide details about the versions you are using, which helps us reproducing
|
||||
and finding the issue quicker. Version information is found in the
|
||||
Home Assistant frontend: Settings -> About.
|
||||
Home Assistant frontend: Configuration -> Info.
|
||||
|
||||
Browser version and operating system is important! Please try to replicate
|
||||
your issue in a different browser and be sure to include your findings.
|
||||
@@ -77,8 +77,8 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
|
||||
## Problem-relevant frontend 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
|
||||
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.
|
||||
-->
|
||||
|
121
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
121
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,121 +0,0 @@
|
||||
name: Report a bug with the UI / Dashboards
|
||||
description: Report an issue related to the Home Assistant frontend.
|
||||
labels: bug
|
||||
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 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: [Settings -> About](https://my.home-assistant.io/redirect/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.
|
||||
render: txt
|
||||
- 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.
|
||||
render: yaml
|
||||
- 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.
|
||||
render: txt
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: >
|
||||
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.
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,17 +1,17 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Request a feature for the UI / Dashboards
|
||||
- name: Request a feature for the UI, Frontend or Lovelace
|
||||
url: https://github.com/home-assistant/frontend/discussions/category_choices
|
||||
about: Request an new feature for the Home Assistant frontend.
|
||||
- name: Report a bug that is NOT related to the UI / Dashboards
|
||||
- name: Report a bug that is NOT related to the UI, Frontend or Lovelace
|
||||
url: https://github.com/home-assistant/core/issues
|
||||
about: This is the issue tracker for our frontend. Please report other issues in the backend ("core") repository.
|
||||
about: This is the issue tracker for our frontend. Please report other issues with the backend repository.
|
||||
- name: Report incorrect or missing information on our website
|
||||
url: https://github.com/home-assistant/home-assistant.io/issues
|
||||
about: Our documentation has its own issue tracker. Please report issues with the website there.
|
||||
- name: I have a question or need support
|
||||
url: https://www.home-assistant.io/help
|
||||
about: We use GitHub for tracking bugs. Check our website for resources on getting help.
|
||||
about: We use GitHub for tracking bugs, check our website for resources on getting help.
|
||||
- name: I'm unsure where to go
|
||||
url: https://www.home-assistant.io/join-chat
|
||||
about: If you are unsure where to go, then joining our chat is recommended; Just ask!
|
||||
|
8
.github/dependabot.yml
vendored
8
.github/dependabot.yml
vendored
@@ -1,8 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
time: "06:00"
|
||||
open-pull-requests-limit: 10
|
86
.github/workflows/cast_deployment.yaml
vendored
86
.github/workflows/cast_deployment.yaml
vendored
@@ -1,86 +0,0 @@
|
||||
name: Cast deployment
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
NODE_VERSION: 16
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
jobs:
|
||||
deploy_dev:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy Development
|
||||
if: github.event_name != 'push'
|
||||
environment:
|
||||
name: Cast Development
|
||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
ref: dev
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v3.6.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
|
||||
- name: Build Cast
|
||||
run: ./node_modules/.bin/gulp build-cast
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Deploy to Netlify
|
||||
id: deploy
|
||||
uses: netlify/actions/cli@master
|
||||
with:
|
||||
args: deploy --dir=cast/dist --alias dev
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_CAST_SITE_ID }}
|
||||
|
||||
deploy_master:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy Production
|
||||
if: github.event_name == 'push'
|
||||
environment:
|
||||
name: Cast Production
|
||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
ref: master
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v3.6.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
|
||||
- name: Build Cast
|
||||
run: ./node_modules/.bin/gulp build-cast
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Deploy to Netlify
|
||||
id: deploy
|
||||
uses: netlify/actions/cli@master
|
||||
with:
|
||||
args: deploy --dir=cast/dist --prod
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_CAST_SITE_ID }}
|
125
.github/workflows/ci.yaml
vendored
125
.github/workflows/ci.yaml
vendored
@@ -10,88 +10,113 @@ on:
|
||||
- dev
|
||||
- master
|
||||
|
||||
env:
|
||||
NODE_VERSION: 16
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint and check format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v3.3.0
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v3.6.0
|
||||
uses: actions/checkout@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
- name: Check for duplicate dependencies
|
||||
run: yarn dedupe --check
|
||||
run: yarn install
|
||||
env:
|
||||
CI: true
|
||||
- name: Build resources
|
||||
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages
|
||||
run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos
|
||||
- name: Run eslint
|
||||
run: yarn run lint:eslint --quiet
|
||||
run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore
|
||||
- name: Run tsc
|
||||
run: yarn run lint:types
|
||||
- name: Run prettier
|
||||
run: yarn run lint:prettier
|
||||
run: ./node_modules/.bin/tsc
|
||||
test:
|
||||
name: Run tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v3.3.0
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v3.6.0
|
||||
uses: actions/checkout@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
- name: Build resources
|
||||
run: ./node_modules/.bin/gulp build-translations build-locale-data
|
||||
- name: Run Tests
|
||||
run: yarn run test
|
||||
run: yarn install
|
||||
env:
|
||||
CI: true
|
||||
- name: Run Mocha
|
||||
run: npm run mocha
|
||||
build:
|
||||
name: Build frontend
|
||||
needs: [lint, test]
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint, test]
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v3.3.0
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v3.6.0
|
||||
uses: actions/checkout@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
run: yarn install
|
||||
env:
|
||||
CI: true
|
||||
- name: Build Application
|
||||
run: ./node_modules/.bin/gulp build-app
|
||||
env:
|
||||
IS_TEST: "true"
|
||||
supervisor:
|
||||
name: Build supervisor
|
||||
needs: [lint, test]
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint, test]
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v3.3.0
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v3.6.0
|
||||
uses: actions/checkout@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
run: yarn install
|
||||
env:
|
||||
CI: true
|
||||
- name: Build Application
|
||||
run: ./node_modules/.bin/gulp build-hassio
|
||||
env:
|
||||
|
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3.3.0
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
@@ -36,14 +36,14 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -57,4 +57,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
39
.github/workflows/demo.yaml
vendored
Normal file
39
.github/workflows/demo.yaml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Demo
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
CI: true
|
||||
- name: Build Demo
|
||||
run: ./node_modules/.bin/gulp build-demo
|
||||
- name: Deploy to Netlify
|
||||
uses: netlify/actions/cli@master
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_DEV_SITE_ID }}
|
||||
with:
|
||||
args: deploy --dir=demo/dist --prod
|
87
.github/workflows/demo_deployment.yaml
vendored
87
.github/workflows/demo_deployment.yaml
vendored
@@ -1,87 +0,0 @@
|
||||
name: Demo deployment
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- master
|
||||
|
||||
env:
|
||||
NODE_VERSION: 16
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
jobs:
|
||||
deploy_dev:
|
||||
runs-on: ubuntu-latest
|
||||
name: Demo Development
|
||||
if: github.event_name != 'push' || github.ref != 'master'
|
||||
environment:
|
||||
name: Demo Development
|
||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
ref: dev
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v3.6.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
|
||||
- name: Build Demo
|
||||
run: ./node_modules/.bin/gulp build-demo
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Deploy to Netlify
|
||||
id: deploy
|
||||
uses: netlify/actions/cli@master
|
||||
with:
|
||||
args: deploy --dir=demo/dist --prod
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_DEV_SITE_ID }}
|
||||
|
||||
deploy_master:
|
||||
runs-on: ubuntu-latest
|
||||
name: Demo Production
|
||||
if: github.event_name == 'push' && github.ref == 'master'
|
||||
environment:
|
||||
name: Demo Production
|
||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
ref: master
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v3.6.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
|
||||
- name: Build Demo
|
||||
run: ./node_modules/.bin/gulp build-demo
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Deploy to Netlify
|
||||
id: deploy
|
||||
uses: netlify/actions/cli@master
|
||||
with:
|
||||
args: deploy --dir=demo/dist --prod
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_SITE_ID }}
|
43
.github/workflows/design_deployment.yaml
vendored
43
.github/workflows/design_deployment.yaml
vendored
@@ -1,43 +0,0 @@
|
||||
name: Design deployment
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
env:
|
||||
NODE_VERSION: 16
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: Design
|
||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v3.3.0
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v3.6.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
|
||||
- name: Build Gallery
|
||||
run: ./node_modules/.bin/gulp build-gallery
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Deploy to Netlify
|
||||
id: deploy
|
||||
uses: netlify/actions/cli@master
|
||||
with:
|
||||
args: deploy --dir=gallery/dist --prod
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_GALLERY_SITE_ID }}
|
52
.github/workflows/design_preview.yaml
vendored
52
.github/workflows/design_preview.yaml
vendored
@@ -1,52 +0,0 @@
|
||||
name: Design preview
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
- labeled
|
||||
branches:
|
||||
- dev
|
||||
|
||||
env:
|
||||
NODE_VERSION: 16
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
jobs:
|
||||
preview:
|
||||
runs-on: ubuntu-latest
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
# Skip running PRs without 'needs design preview' label
|
||||
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v3.3.0
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v3.6.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
|
||||
- name: Build Gallery
|
||||
run: ./node_modules/.bin/gulp build-gallery
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Deploy preview to Netlify
|
||||
id: deploy
|
||||
uses: netlify/actions/cli@master
|
||||
with:
|
||||
args: deploy --dir=gallery/dist --alias "deploy-preview-${{ github.event.number }}"
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_GALLERY_SITE_ID }}
|
||||
|
||||
- name: Generate summary
|
||||
run: |
|
||||
echo "${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}" >> "$GITHUB_STEP_SUMMARY"
|
2
.github/workflows/lock.yml
vendored
2
.github/workflows/lock.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v4.0.0
|
||||
- uses: dessant/lock-threads@v2.0.1
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-lock-inactive-days: "30"
|
||||
|
72
.github/workflows/nightly.yaml
vendored
72
.github/workflows/nightly.yaml
vendored
@@ -1,72 +0,0 @@
|
||||
name: Nightly
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 1 * * *"
|
||||
|
||||
env:
|
||||
PYTHON_VERSION: "3.10"
|
||||
NODE_VERSION: 16
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
permissions:
|
||||
actions: none
|
||||
|
||||
jobs:
|
||||
nightly:
|
||||
name: Nightly
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v3.3.0
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v3.6.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: Download translations
|
||||
run: ./script/translations_download
|
||||
env:
|
||||
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }}
|
||||
|
||||
- name: Bump version
|
||||
run: script/version_bump.js nightly
|
||||
|
||||
- name: Build nightly Python wheels
|
||||
run: |
|
||||
pip install build
|
||||
yarn install
|
||||
export SKIP_FETCH_NIGHTLY_TRANSLATIONS=1
|
||||
script/build_frontend
|
||||
rm -rf dist home_assistant_frontend.egg-info
|
||||
python3 -m build
|
||||
|
||||
- name: Archive translations
|
||||
run: tar -czvf translations.tar.gz translations
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
path: dist/home_assistant_frontend*.whl
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload translations
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: translations
|
||||
path: translations.tar.gz
|
||||
if-no-files-found: error
|
84
.github/workflows/release.yaml
vendored
84
.github/workflows/release.yaml
vendored
@@ -1,84 +0,0 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
env:
|
||||
PYTHON_VERSION: "3.10"
|
||||
NODE_VERSION: 16
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
# Set default workflow permissions
|
||||
# All scopes not mentioned here are set to no access
|
||||
# https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token
|
||||
permissions:
|
||||
actions: none
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # Required to upload release assets
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v3.3.0
|
||||
|
||||
- name: Verify version
|
||||
uses: home-assistant/actions/helpers/verify-version@master
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v3.6.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: Download Translations
|
||||
run: ./script/translations_download
|
||||
env:
|
||||
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }}
|
||||
- name: Build and release package
|
||||
run: |
|
||||
python3 -m pip install twine build
|
||||
export TWINE_USERNAME="__token__"
|
||||
export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}"
|
||||
export SKIP_FETCH_NIGHTLY_TRANSLATIONS=1
|
||||
script/release
|
||||
|
||||
- name: Upload release assets
|
||||
uses: softprops/action-gh-release@v0.1.15
|
||||
with:
|
||||
files: |
|
||||
dist/*.whl
|
||||
dist/*.tar.gz
|
||||
|
||||
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: Build wheels
|
||||
uses: home-assistant/wheels@2022.10.1
|
||||
with:
|
||||
abi: cp310
|
||||
tag: musllinux_1_2
|
||||
arch: amd64
|
||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||
requirements: "requirements.txt"
|
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 90 days stale policy
|
||||
uses: actions/stale@v7.0.0
|
||||
uses: actions/stale@v3.0.13
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 90
|
||||
|
25
.github/workflows/translations.yaml
vendored
25
.github/workflows/translations.yaml
vendored
@@ -1,25 +0,0 @@
|
||||
name: Translations
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
paths:
|
||||
- src/translations/en.json
|
||||
|
||||
env:
|
||||
NODE_VERSION: 16
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
name: Upload
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v3.3.0
|
||||
|
||||
- name: Upload Translations
|
||||
run: |
|
||||
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
|
||||
|
||||
./script/translations_upload_base
|
35
.gitignore
vendored
35
.gitignore
vendored
@@ -1,23 +1,10 @@
|
||||
.DS_Store
|
||||
.reify-cache
|
||||
|
||||
# build
|
||||
build/
|
||||
dist/
|
||||
/hass_frontend/
|
||||
/translations/
|
||||
|
||||
# yarn
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/releases
|
||||
!.yarn/plugins
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
.pnp.*
|
||||
/node_modules/
|
||||
yarn-error.log
|
||||
build
|
||||
build-translations/*
|
||||
node_modules/*
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
hass_frontend/*
|
||||
.reify-cache
|
||||
|
||||
# Python stuff
|
||||
*.py[cod]
|
||||
@@ -27,8 +14,11 @@ npm-debug.log
|
||||
# venv stuff
|
||||
pyvenv.cfg
|
||||
pip-selfcheck.json
|
||||
/venv/
|
||||
venv
|
||||
.venv
|
||||
lib
|
||||
bin
|
||||
dist
|
||||
|
||||
# vscode
|
||||
.vscode/*
|
||||
@@ -41,9 +31,10 @@ src/cast/dev_const.ts
|
||||
|
||||
# Secrets
|
||||
.lokalise_token
|
||||
yarn-error.log
|
||||
|
||||
# asdf
|
||||
#asdf
|
||||
.tool-versions
|
||||
|
||||
# Home Assistant config
|
||||
/config/
|
||||
/config
|
||||
|
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
yarn run lint-staged --relative --shell "/bin/bash"
|
@@ -1,4 +1,5 @@
|
||||
build
|
||||
build-translations/*
|
||||
translations/*
|
||||
node_modules/*
|
||||
hass_frontend/*
|
||||
|
5
.vscode/extensions.json
vendored
5
.vscode/extensions.json
vendored
@@ -2,8 +2,7 @@
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"runem.lit-plugin",
|
||||
"github.vscode-pull-request-github",
|
||||
"eamodio.gitlens"
|
||||
"bierner.lit-html",
|
||||
"runem.lit-plugin"
|
||||
]
|
||||
}
|
||||
|
10
.vscode/tasks.json
vendored
10
.vscode/tasks.json
vendored
@@ -181,7 +181,7 @@
|
||||
{
|
||||
"label": "Run HA Core for Supervisor in devcontainer",
|
||||
"type": "shell",
|
||||
"command": "SUPERVISOR=${input:supervisorHost} SUPERVISOR_TOKEN=${input:supervisorToken} script/core",
|
||||
"command": "HASSIO=${input:supervisorHost} HASSIO_TOKEN=${input:supervisorToken} script/core",
|
||||
"isBackground": true,
|
||||
"group": {
|
||||
"kind": "build",
|
||||
@@ -191,13 +191,7 @@
|
||||
"runOptions": {
|
||||
"instanceLimit": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Setup and fetch nightly translations",
|
||||
"type": "gulp",
|
||||
"task": "setup-and-fetch-nightly-translations",
|
||||
"problemMatcher": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
|
@@ -1,34 +0,0 @@
|
||||
diff --git a/lib/legacy/class.js b/lib/legacy/class.js
|
||||
index aee2511be1cd9bf900ee552bc98190c1631c57c0..f2f499d68bf52034cac9c28307c99e8ce6b8417d 100644
|
||||
--- a/lib/legacy/class.js
|
||||
+++ b/lib/legacy/class.js
|
||||
@@ -304,17 +304,23 @@ function GenerateClassFromInfo(info, Base, behaviors) {
|
||||
// only proceed if the generated class' prototype has not been registered.
|
||||
const generatedProto = PolymerGenerated.prototype;
|
||||
if (!generatedProto.hasOwnProperty(JSCompiler_renameProperty('__hasRegisterFinished', generatedProto))) {
|
||||
- generatedProto.__hasRegisterFinished = true;
|
||||
+ // make sure legacy lifecycle is called on the *element*'s prototype
|
||||
+ // and not the generated class prototype; if the element has been
|
||||
+ // extended, these are *not* the same.
|
||||
+ const proto = Object.getPrototypeOf(this);
|
||||
+ // Only set flag when generated prototype itself is registered,
|
||||
+ // as this element may be extended from, and needs to run `registered`
|
||||
+ // on all behaviors on the subclass as well.
|
||||
+ if (proto === generatedProto) {
|
||||
+ generatedProto.__hasRegisterFinished = true;
|
||||
+ }
|
||||
// ensure superclass is registered first.
|
||||
super._registered();
|
||||
// copy properties onto the generated class lazily if we're optimizing,
|
||||
- if (legacyOptimizations) {
|
||||
+ if (legacyOptimizations && !Object.hasOwnProperty(generatedProto, '__hasCopiedProperties')) {
|
||||
+ generatedProto.__hasCopiedProperties = true;
|
||||
copyPropertiesToProto(generatedProto);
|
||||
}
|
||||
- // make sure legacy lifecycle is called on the *element*'s prototype
|
||||
- // and not the generated class prototype; if the element has been
|
||||
- // extended, these are *not* the same.
|
||||
- const proto = Object.getPrototypeOf(this);
|
||||
let list = lifecycle.beforeRegister;
|
||||
if (list) {
|
||||
for (let i=0; i < list.length; i++) {
|
541
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
541
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
File diff suppressed because one or more lines are too long
9
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
9
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
File diff suppressed because one or more lines are too long
873
.yarn/releases/yarn-3.5.0.cjs
vendored
873
.yarn/releases/yarn-3.5.0.cjs
vendored
File diff suppressed because one or more lines are too long
11
.yarnrc.yml
11
.yarnrc.yml
@@ -1,11 +0,0 @@
|
||||
defaultSemverRangePrefix: ""
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
|
||||
spec: "@yarnpkg/plugin-typescript"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.5.0.cjs
|
@@ -1,4 +1,5 @@
|
||||
include README.md
|
||||
include LICENSE.md
|
||||
graft hass_frontend
|
||||
graft hass_frontend_es5
|
||||
recursive-exclude * *.py[co]
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
This is the repository for the official [Home Assistant](https://home-assistant.io) frontend.
|
||||
|
||||
[](https://demo.home-assistant.io/)
|
||||
[](https://demo.home-assistant.io/)
|
||||
|
||||
- [View demo of Home Assistant](https://demo.home-assistant.io/)
|
||||
- [More information about Home Assistant](https://home-assistant.io)
|
||||
|
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'
|
7
build-scripts/.eslintrc
Normal file
7
build-scripts/.eslintrc
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"rules": {
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"no-restricted-syntax": 0,
|
||||
"no-console": 0
|
||||
}
|
||||
}
|
@@ -1,12 +1,7 @@
|
||||
{
|
||||
"extends": "../.eslintrc.json",
|
||||
"rules": {
|
||||
"no-console": "off",
|
||||
"import/no-extraneous-dependencies": "off",
|
||||
"import/extensions": "off",
|
||||
"import/no-dynamic-require": "off",
|
||||
"global-require": "off",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"prefer-arrow-callback": "off"
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"global-require": 0
|
||||
}
|
||||
}
|
||||
|
@@ -1,168 +0,0 @@
|
||||
const path = require("path");
|
||||
|
||||
// Currently only supports CommonJS modules, as require is synchronous. `import` would need babel running asynchronous.
|
||||
module.exports = function inlineConstants(babel, options, cwd) {
|
||||
const t = babel.types;
|
||||
|
||||
if (!Array.isArray(options.modules)) {
|
||||
throw new TypeError(
|
||||
"babel-plugin-inline-constants: expected a `modules` array to be passed"
|
||||
);
|
||||
}
|
||||
|
||||
if (options.resolveExtensions && !Array.isArray(options.resolveExtensions)) {
|
||||
throw new TypeError(
|
||||
"babel-plugin-inline-constants: expected `resolveExtensions` to be an array"
|
||||
);
|
||||
}
|
||||
|
||||
const ignoreModuleNotFound = options.ignoreModuleNotFound;
|
||||
const resolveExtensions = options.resolveExtensions;
|
||||
|
||||
const hasRelativeModules = options.modules.some(
|
||||
(module) => module.startsWith(".") || module.startsWith("/")
|
||||
);
|
||||
|
||||
const modules = Object.fromEntries(
|
||||
options.modules.map((module) => {
|
||||
const absolute = module.startsWith(".")
|
||||
? require.resolve(module, { paths: [cwd] })
|
||||
: module;
|
||||
return [absolute, require(absolute)];
|
||||
})
|
||||
);
|
||||
|
||||
const toLiteral = (value) => {
|
||||
if (typeof value === "string") {
|
||||
return t.stringLiteral(value);
|
||||
}
|
||||
|
||||
if (typeof value === "number") {
|
||||
return t.numericLiteral(value);
|
||||
}
|
||||
|
||||
if (typeof value === "boolean") {
|
||||
return t.booleanLiteral(value);
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
return t.nullLiteral();
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"babel-plugin-inline-constants: cannot handle non-literal `" + value + "`"
|
||||
);
|
||||
};
|
||||
|
||||
const resolveAbsolute = (value, state, resolveExtensionIndex) => {
|
||||
if (!state.filename) {
|
||||
throw new TypeError(
|
||||
"babel-plugin-inline-constants: expected a `filename` to be set for files"
|
||||
);
|
||||
}
|
||||
|
||||
if (resolveExtensions && resolveExtensionIndex !== undefined) {
|
||||
value += resolveExtensions[resolveExtensionIndex];
|
||||
}
|
||||
|
||||
try {
|
||||
return require.resolve(value, { paths: [path.dirname(state.filename)] });
|
||||
} catch (error) {
|
||||
if (
|
||||
error.code === "MODULE_NOT_FOUND" &&
|
||||
resolveExtensions &&
|
||||
(resolveExtensionIndex === undefined ||
|
||||
resolveExtensionIndex < resolveExtensions.length - 1)
|
||||
) {
|
||||
const resolveExtensionIdx = (resolveExtensionIndex || -1) + 1;
|
||||
return resolveAbsolute(value, state, resolveExtensionIdx);
|
||||
}
|
||||
|
||||
if (error.code === "MODULE_NOT_FOUND" && ignoreModuleNotFound) {
|
||||
return undefined;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const importDeclaration = (p, state) => {
|
||||
if (p.node.type !== "ImportDeclaration") {
|
||||
return;
|
||||
}
|
||||
const absolute =
|
||||
hasRelativeModules && p.node.source.value.startsWith(".")
|
||||
? resolveAbsolute(p.node.source.value, state)
|
||||
: p.node.source.value;
|
||||
|
||||
if (!absolute || !(absolute in modules)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const module = modules[absolute];
|
||||
|
||||
for (const specifier of p.node.specifiers) {
|
||||
if (
|
||||
specifier.type === "ImportDefaultSpecifier" &&
|
||||
specifier.local &&
|
||||
specifier.local.type === "Identifier"
|
||||
) {
|
||||
if (!("default" in module)) {
|
||||
throw new Error(
|
||||
"babel-plugin-inline-constants: cannot access default export from `" +
|
||||
p.node.source.value +
|
||||
"`"
|
||||
);
|
||||
}
|
||||
|
||||
const variableValue = toLiteral(module.default);
|
||||
const variable = t.variableDeclarator(
|
||||
t.identifier(specifier.local.name),
|
||||
variableValue
|
||||
);
|
||||
|
||||
p.insertBefore({
|
||||
type: "VariableDeclaration",
|
||||
kind: "const",
|
||||
declarations: [variable],
|
||||
});
|
||||
} else if (
|
||||
specifier.type === "ImportSpecifier" &&
|
||||
specifier.imported &&
|
||||
specifier.imported.type === "Identifier" &&
|
||||
specifier.local &&
|
||||
specifier.local.type === "Identifier"
|
||||
) {
|
||||
if (!(specifier.imported.name in module)) {
|
||||
throw new Error(
|
||||
"babel-plugin-inline-constants: cannot access `" +
|
||||
specifier.imported.name +
|
||||
"` from `" +
|
||||
p.node.source.value +
|
||||
"`"
|
||||
);
|
||||
}
|
||||
|
||||
const variableValue = toLiteral(module[specifier.imported.name]);
|
||||
const variable = t.variableDeclarator(
|
||||
t.identifier(specifier.local.name),
|
||||
variableValue
|
||||
);
|
||||
|
||||
p.insertBefore({
|
||||
type: "VariableDeclaration",
|
||||
kind: "const",
|
||||
declarations: [variable],
|
||||
});
|
||||
} else {
|
||||
throw new Error("Cannot handle specifier `" + specifier.type + "`");
|
||||
}
|
||||
}
|
||||
p.remove();
|
||||
};
|
||||
|
||||
return {
|
||||
visitor: {
|
||||
ImportDeclaration: importDeclaration,
|
||||
},
|
||||
};
|
||||
};
|
@@ -2,24 +2,16 @@ const path = require("path");
|
||||
const env = require("./env.js");
|
||||
const paths = require("./paths.js");
|
||||
|
||||
// GitHub base URL to use for production source maps
|
||||
// Nightly builds use the commit SHA, otherwise assumes there is a tag that matches the version
|
||||
module.exports.sourceMapURL = () => {
|
||||
const ref = env.version().endsWith("dev")
|
||||
? process.env.GITHUB_SHA || "dev"
|
||||
: env.version();
|
||||
return `https://raw.githubusercontent.com/home-assistant/frontend/${ref}`;
|
||||
};
|
||||
|
||||
// Files from NPM Packages that should not be imported
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
module.exports.ignorePackages = ({ latestBuild }) => [
|
||||
// Bloats bundle and it's not used.
|
||||
path.resolve(require.resolve("moment"), "../locale"),
|
||||
// Part of yaml.js and only used for !!js functions that we don't use
|
||||
require.resolve("esprima"),
|
||||
];
|
||||
|
||||
// Files from NPM packages that we should replace with empty file
|
||||
module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
|
||||
module.exports.emptyPackages = ({ latestBuild }) =>
|
||||
[
|
||||
// Contains all color definitions for all material color sets.
|
||||
// We don't use it
|
||||
@@ -27,8 +19,7 @@ module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
|
||||
require.resolve("@polymer/paper-styles/default-theme.js"),
|
||||
// Loads stuff from a CDN
|
||||
require.resolve("@polymer/font-roboto/roboto.js"),
|
||||
require.resolve("@vaadin/vaadin-material-styles/typography.js"),
|
||||
require.resolve("@vaadin/vaadin-material-styles/font-icons.js"),
|
||||
require.resolve("@vaadin/vaadin-material-styles/font-roboto.js"),
|
||||
// Compatibility not needed for latest builds
|
||||
latestBuild &&
|
||||
// wrapped in require.resolve so it blows up if file no longer exists
|
||||
@@ -37,15 +28,6 @@ module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
|
||||
),
|
||||
// This polyfill is loaded in workers to support ES5, filter it out.
|
||||
latestBuild && require.resolve("proxy-polyfill/src/index.js"),
|
||||
// Icons in supervisor conflict with icons in HA so we don't load.
|
||||
isHassioBuild &&
|
||||
require.resolve(
|
||||
path.resolve(paths.polymer_dir, "src/components/ha-icon.ts")
|
||||
),
|
||||
isHassioBuild &&
|
||||
require.resolve(
|
||||
path.resolve(paths.polymer_dir, "src/components/ha-icon-picker.ts")
|
||||
),
|
||||
].filter(Boolean);
|
||||
|
||||
module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
|
||||
@@ -53,7 +35,6 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
|
||||
__BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
|
||||
__VERSION__: JSON.stringify(env.version()),
|
||||
__DEMO__: false,
|
||||
__SUPERVISOR__: false,
|
||||
__BACKWARDS_COMPAT__: false,
|
||||
__STATIC_PATH__: "/static/",
|
||||
"process.env.NODE_ENV": JSON.stringify(
|
||||
@@ -62,50 +43,25 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
|
||||
...defineOverlay,
|
||||
});
|
||||
|
||||
const htmlMinifierOptions = {
|
||||
caseSensitive: true,
|
||||
collapseWhitespace: true,
|
||||
conservativeCollapse: true,
|
||||
decodeEntities: true,
|
||||
removeComments: true,
|
||||
removeRedundantAttributes: true,
|
||||
minifyCSS: {
|
||||
level: 0,
|
||||
},
|
||||
};
|
||||
|
||||
module.exports.terserOptions = ({ latestBuild, isTestBuild }) => ({
|
||||
module.exports.terserOptions = (latestBuild) => ({
|
||||
safari10: !latestBuild,
|
||||
ecma: latestBuild ? undefined : 5,
|
||||
format: { comments: false },
|
||||
sourceMap: !isTestBuild,
|
||||
output: { comments: false },
|
||||
});
|
||||
|
||||
module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
|
||||
module.exports.babelOptions = ({ latestBuild }) => ({
|
||||
babelrc: false,
|
||||
compact: false,
|
||||
presets: [
|
||||
!latestBuild && [
|
||||
"@babel/preset-env",
|
||||
require("@babel/preset-env").default,
|
||||
{
|
||||
useBuiltIns: "entry",
|
||||
corejs: { version: "3.29", proposals: true },
|
||||
bugfixes: true,
|
||||
corejs: "3.6",
|
||||
},
|
||||
],
|
||||
"@babel/preset-typescript",
|
||||
require("@babel/preset-typescript").default,
|
||||
].filter(Boolean),
|
||||
plugins: [
|
||||
[
|
||||
path.resolve(
|
||||
paths.polymer_dir,
|
||||
"build-scripts/babel-plugins/inline-constants-plugin.js"
|
||||
),
|
||||
{
|
||||
modules: ["@mdi/js"],
|
||||
ignoreModuleNotFound: true,
|
||||
},
|
||||
],
|
||||
// Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2})
|
||||
!latestBuild && [
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
@@ -114,42 +70,24 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
|
||||
// Only support the syntax, Webpack will handle it.
|
||||
"@babel/plugin-syntax-import-meta",
|
||||
"@babel/plugin-syntax-dynamic-import",
|
||||
"@babel/plugin-syntax-top-level-await",
|
||||
// Support various proposals
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],
|
||||
"@babel/plugin-proposal-class-static-block",
|
||||
["@babel/plugin-proposal-private-methods", { loose: true }],
|
||||
["@babel/plugin-proposal-private-property-in-object", { loose: true }],
|
||||
["@babel/plugin-proposal-class-properties", { loose: true }],
|
||||
// Minify template literals for production
|
||||
isProdBuild && [
|
||||
"template-html-minifier",
|
||||
{
|
||||
modules: {
|
||||
lit: [
|
||||
"html",
|
||||
{ name: "svg", encapsulation: "svg" },
|
||||
{ name: "css", encapsulation: "style" },
|
||||
],
|
||||
"@polymer/polymer/lib/utils/html-tag": ["html"],
|
||||
},
|
||||
strictCSS: true,
|
||||
htmlMinifier: htmlMinifierOptions,
|
||||
failOnError: true, // we can turn this off in case of false positives
|
||||
},
|
||||
[
|
||||
require("@babel/plugin-proposal-decorators").default,
|
||||
{ decoratorsBeforeExport: true },
|
||||
],
|
||||
[
|
||||
require("@babel/plugin-proposal-class-properties").default,
|
||||
{ loose: true },
|
||||
],
|
||||
].filter(Boolean),
|
||||
exclude: [
|
||||
// \\ for Windows, / for Mac OS and Linux
|
||||
/node_modules[\\/]core-js/,
|
||||
/node_modules[\\/]webpack[\\/]buildin/,
|
||||
],
|
||||
sourceMaps: !isTestBuild,
|
||||
});
|
||||
|
||||
const nameSuffix = (latestBuild) => (latestBuild ? "-latest" : "-es5");
|
||||
// Are already ES5, cause warnings when babelified.
|
||||
module.exports.babelExclude = () => [
|
||||
require.resolve("@mdi/js/mdi.js"),
|
||||
require.resolve("hls.js"),
|
||||
];
|
||||
|
||||
const outputPath = (outputRoot, latestBuild) =>
|
||||
path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
|
||||
@@ -173,17 +111,14 @@ BundleConfig {
|
||||
latestBuild: boolean,
|
||||
// If we're doing a stats build (create nice chunk names)
|
||||
isStatsBuild: boolean,
|
||||
// If it's just a test build in CI, skip time on source map generation
|
||||
isTestBuild: boolean,
|
||||
// Names of entrypoints that should not be hashed
|
||||
dontHash: Set<string>
|
||||
}
|
||||
*/
|
||||
|
||||
module.exports.config = {
|
||||
app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild, isWDS }) {
|
||||
app({ isProdBuild, latestBuild, isStatsBuild, isWDS }) {
|
||||
return {
|
||||
name: "app" + nameSuffix(latestBuild),
|
||||
entry: {
|
||||
service_worker: "./src/entrypoints/service_worker.ts",
|
||||
app: "./src/entrypoints/app.ts",
|
||||
@@ -197,14 +132,12 @@ module.exports.config = {
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
isStatsBuild,
|
||||
isTestBuild,
|
||||
isWDS,
|
||||
};
|
||||
},
|
||||
|
||||
demo({ isProdBuild, latestBuild, isStatsBuild }) {
|
||||
return {
|
||||
name: "demo" + nameSuffix(latestBuild),
|
||||
entry: {
|
||||
main: path.resolve(paths.demo_dir, "src/entrypoint.ts"),
|
||||
},
|
||||
@@ -223,7 +156,6 @@ module.exports.config = {
|
||||
cast({ isProdBuild, latestBuild }) {
|
||||
const entry = {
|
||||
launcher: path.resolve(paths.cast_dir, "src/launcher/entrypoint.ts"),
|
||||
media: path.resolve(paths.cast_dir, "src/media/entrypoint.ts"),
|
||||
};
|
||||
|
||||
if (latestBuild) {
|
||||
@@ -234,7 +166,6 @@ module.exports.config = {
|
||||
}
|
||||
|
||||
return {
|
||||
name: "cast" + nameSuffix(latestBuild),
|
||||
entry,
|
||||
outputPath: outputPath(paths.cast_output_root, latestBuild),
|
||||
publicPath: publicPath(latestBuild),
|
||||
@@ -246,9 +177,8 @@ module.exports.config = {
|
||||
};
|
||||
},
|
||||
|
||||
hassio({ isProdBuild, latestBuild, isStatsBuild, isTestBuild }) {
|
||||
hassio({ isProdBuild, latestBuild }) {
|
||||
return {
|
||||
name: "supervisor" + nameSuffix(latestBuild),
|
||||
entry: {
|
||||
entrypoint: path.resolve(paths.hassio_dir, "src/entrypoint.ts"),
|
||||
},
|
||||
@@ -256,18 +186,11 @@ module.exports.config = {
|
||||
publicPath: publicPath(latestBuild, paths.hassio_publicPath),
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
isStatsBuild,
|
||||
isTestBuild,
|
||||
isHassioBuild: true,
|
||||
defineOverlay: {
|
||||
__SUPERVISOR__: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
gallery({ isProdBuild, latestBuild }) {
|
||||
return {
|
||||
name: "gallery" + nameSuffix(latestBuild),
|
||||
entry: {
|
||||
entrypoint: path.resolve(paths.gallery_dir, "src/entrypoint.js"),
|
||||
},
|
||||
@@ -275,9 +198,6 @@ module.exports.config = {
|
||||
publicPath: publicPath(latestBuild),
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
defineOverlay: {
|
||||
__DEMO__: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
@@ -17,7 +17,7 @@ module.exports = {
|
||||
isStatsBuild() {
|
||||
return process.env.STATS === "1";
|
||||
},
|
||||
isTestBuild() {
|
||||
isTest() {
|
||||
return process.env.IS_TEST === "true";
|
||||
},
|
||||
isNetlify() {
|
||||
@@ -25,11 +25,11 @@ module.exports = {
|
||||
},
|
||||
version() {
|
||||
const version = fs
|
||||
.readFileSync(path.resolve(paths.polymer_dir, "pyproject.toml"), "utf8")
|
||||
.match(/version\W+=\W"(\d{8}\.\d(?:\.dev)?)"/);
|
||||
.readFileSync(path.resolve(paths.polymer_dir, "setup.py"), "utf8")
|
||||
.match(/\d{8}\.\d+/);
|
||||
if (!version) {
|
||||
throw Error("Version not found");
|
||||
}
|
||||
return version[1];
|
||||
return version[0];
|
||||
},
|
||||
};
|
||||
|
@@ -1,10 +1,10 @@
|
||||
// Run HA develop mode
|
||||
|
||||
const gulp = require("gulp");
|
||||
|
||||
const env = require("../env");
|
||||
|
||||
require("./clean.js");
|
||||
require("./translations.js");
|
||||
require("./locale-data.js");
|
||||
require("./gen-icons-json.js");
|
||||
require("./gather-static.js");
|
||||
require("./compress.js");
|
||||
@@ -26,8 +26,7 @@ gulp.task(
|
||||
"gen-icons-json",
|
||||
"gen-pages-dev",
|
||||
"gen-index-app-dev",
|
||||
"build-translations",
|
||||
"build-locale-data"
|
||||
"build-translations"
|
||||
),
|
||||
"copy-static-app",
|
||||
env.useWDS()
|
||||
@@ -45,11 +44,11 @@ gulp.task(
|
||||
process.env.NODE_ENV = "production";
|
||||
},
|
||||
"clean",
|
||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
"copy-static-app",
|
||||
env.useRollup() ? "rollup-prod-app" : "webpack-prod-app",
|
||||
// Don't compress running tests
|
||||
...(env.isTestBuild() ? [] : ["compress-app"]),
|
||||
...// Don't compress running tests
|
||||
(env.isTest() ? [] : ["compress-app"]),
|
||||
gulp.parallel(
|
||||
"gen-pages-prod",
|
||||
"gen-index-app-prod",
|
||||
|
@@ -18,7 +18,7 @@ gulp.task(
|
||||
},
|
||||
"clean-cast",
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
"copy-static-cast",
|
||||
"gen-index-cast-dev",
|
||||
env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast"
|
||||
@@ -33,7 +33,7 @@ gulp.task(
|
||||
},
|
||||
"clean-cast",
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
"copy-static-cast",
|
||||
env.useRollup() ? "rollup-prod-cast" : "webpack-prod-cast",
|
||||
"gen-index-cast-prod"
|
||||
|
@@ -1,40 +1,36 @@
|
||||
const del = import("del");
|
||||
const del = require("del");
|
||||
const gulp = require("gulp");
|
||||
const paths = require("../paths");
|
||||
require("./translations");
|
||||
|
||||
gulp.task(
|
||||
"clean",
|
||||
gulp.parallel("clean-translations", async () =>
|
||||
(await del).deleteSync([paths.app_output_root, paths.build_dir])
|
||||
)
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.app_output_root, paths.build_dir]);
|
||||
})
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"clean-demo",
|
||||
gulp.parallel("clean-translations", async () =>
|
||||
(await del).deleteSync([paths.demo_output_root, paths.build_dir])
|
||||
)
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.demo_output_root, paths.build_dir]);
|
||||
})
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"clean-cast",
|
||||
gulp.parallel("clean-translations", async () =>
|
||||
(await del).deleteSync([paths.cast_output_root, paths.build_dir])
|
||||
)
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.cast_output_root, paths.build_dir]);
|
||||
})
|
||||
);
|
||||
|
||||
gulp.task("clean-hassio", async () =>
|
||||
(await del).deleteSync([paths.hassio_output_root, paths.build_dir])
|
||||
);
|
||||
gulp.task("clean-hassio", function cleanOutputAndBuildDir() {
|
||||
return del([paths.hassio_output_root, paths.build_dir]);
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
"clean-gallery",
|
||||
gulp.parallel("clean-translations", async () =>
|
||||
(await del).deleteSync([
|
||||
paths.gallery_output_root,
|
||||
paths.gallery_build,
|
||||
paths.build_dir,
|
||||
])
|
||||
)
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.gallery_output_root, paths.build_dir]);
|
||||
})
|
||||
);
|
||||
|
@@ -20,12 +20,7 @@ gulp.task(
|
||||
},
|
||||
"clean-demo",
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel(
|
||||
"gen-icons-json",
|
||||
"gen-index-demo-dev",
|
||||
"build-translations",
|
||||
"build-locale-data"
|
||||
),
|
||||
gulp.parallel("gen-icons-json", "gen-index-demo-dev", "build-translations"),
|
||||
"copy-static-demo",
|
||||
env.useRollup() ? "rollup-dev-server-demo" : "webpack-dev-server-demo"
|
||||
)
|
||||
@@ -40,7 +35,7 @@ gulp.task(
|
||||
"clean-demo",
|
||||
// Cast needs to be backwards compatible and older HA has no translations
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
"copy-static-demo",
|
||||
env.useRollup() ? "rollup-prod-demo" : "webpack-prod-demo",
|
||||
"gen-index-demo-prod"
|
||||
|
@@ -1,12 +1,17 @@
|
||||
const del = require("del");
|
||||
const gulp = require("gulp");
|
||||
const fs = require("fs/promises");
|
||||
const fs = require("fs");
|
||||
const mapStream = require("map-stream");
|
||||
|
||||
const inDirFrontend = "translations/frontend";
|
||||
const inDirBackend = "translations/backend";
|
||||
const downloadDir = "translations/downloads";
|
||||
const srcMeta = "src/translations/translationMetadata.json";
|
||||
|
||||
const encoding = "utf8";
|
||||
|
||||
const tasks = [];
|
||||
|
||||
function hasHtml(data) {
|
||||
return /<[a-z][\s\S]*>/i.test(data);
|
||||
}
|
||||
@@ -41,29 +46,50 @@ function checkHtml() {
|
||||
});
|
||||
}
|
||||
|
||||
// Backend translations do not currently pass HTML check so are excluded here for now
|
||||
gulp.task("check-translations-html", function () {
|
||||
return gulp.src([`${inDirFrontend}/*.json`]).pipe(checkHtml());
|
||||
let taskName = "clean-downloaded-translations";
|
||||
gulp.task(taskName, function () {
|
||||
return del([`${downloadDir}/**`]);
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
gulp.task("check-all-files-exist", async function () {
|
||||
const file = await fs.readFile(srcMeta, { encoding });
|
||||
taskName = "check-translations-html";
|
||||
gulp.task(taskName, function () {
|
||||
return gulp.src(`${downloadDir}/*.json`).pipe(checkHtml());
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
taskName = "check-all-files-exist";
|
||||
gulp.task(taskName, function () {
|
||||
const file = fs.readFileSync(srcMeta, { encoding });
|
||||
const meta = JSON.parse(file);
|
||||
const writings = [];
|
||||
Object.keys(meta).forEach((lang) => {
|
||||
writings.push(
|
||||
fs.writeFile(`${inDirFrontend}/${lang}.json`, JSON.stringify({}), {
|
||||
flag: "wx",
|
||||
}),
|
||||
fs.writeFile(`${inDirBackend}/${lang}.json`, JSON.stringify({}), {
|
||||
flag: "wx",
|
||||
})
|
||||
);
|
||||
if (!fs.existsSync(`${inDirFrontend}/${lang}.json`)) {
|
||||
fs.writeFileSync(`${inDirFrontend}/${lang}.json`, JSON.stringify({}));
|
||||
}
|
||||
if (!fs.existsSync(`${inDirBackend}/${lang}.json`)) {
|
||||
fs.writeFileSync(`${inDirBackend}/${lang}.json`, JSON.stringify({}));
|
||||
}
|
||||
});
|
||||
await Promise.allSettled(writings);
|
||||
return Promise.resolve();
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
taskName = "move-downloaded-translations";
|
||||
gulp.task(taskName, function () {
|
||||
return gulp.src(`${downloadDir}/*.json`).pipe(gulp.dest(inDirFrontend));
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
taskName = "check-downloaded-translations";
|
||||
gulp.task(
|
||||
"check-downloaded-translations",
|
||||
gulp.series("check-translations-html", "check-all-files-exist")
|
||||
taskName,
|
||||
gulp.series(
|
||||
"check-translations-html",
|
||||
"move-downloaded-translations",
|
||||
"check-all-files-exist",
|
||||
"clean-downloaded-translations"
|
||||
)
|
||||
);
|
||||
tasks.push(taskName);
|
||||
|
||||
module.exports = tasks;
|
||||
|
@@ -1,4 +1,6 @@
|
||||
// Tasks to generate entry HTML
|
||||
/* eslint-disable import/no-dynamic-require */
|
||||
/* eslint-disable global-require */
|
||||
const gulp = require("gulp");
|
||||
const fs = require("fs-extra");
|
||||
const path = require("path");
|
||||
@@ -89,9 +91,7 @@ gulp.task("gen-pages-prod", (done) => {
|
||||
});
|
||||
|
||||
gulp.task("gen-index-app-dev", (done) => {
|
||||
let latestAppJS;
|
||||
let latestCoreJS;
|
||||
let latestCustomPanelJS;
|
||||
let latestAppJS, latestCoreJS, latestCustomPanelJS;
|
||||
|
||||
if (env.useWDS()) {
|
||||
latestAppJS = "http://localhost:8000/src/entrypoints/app.ts";
|
||||
@@ -154,15 +154,6 @@ gulp.task("gen-index-cast-dev", (done) => {
|
||||
contentReceiver
|
||||
);
|
||||
|
||||
const contentMedia = renderCastTemplate("media", {
|
||||
latestMediaJS: "/frontend_latest/media.js",
|
||||
es5MediaJS: "/frontend_es5/media.js",
|
||||
});
|
||||
fs.outputFileSync(
|
||||
path.resolve(paths.cast_output_root, "media.html"),
|
||||
contentMedia
|
||||
);
|
||||
|
||||
const contentFAQ = renderCastTemplate("launcher-faq", {
|
||||
latestLauncherJS: "/frontend_latest/launcher.js",
|
||||
es5LauncherJS: "/frontend_es5/launcher.js",
|
||||
@@ -201,15 +192,6 @@ gulp.task("gen-index-cast-prod", (done) => {
|
||||
contentReceiver
|
||||
);
|
||||
|
||||
const contentMedia = renderCastTemplate("media", {
|
||||
latestMediaJS: latestManifest["media.js"],
|
||||
es5MediaJS: es5Manifest["media.js"],
|
||||
});
|
||||
fs.outputFileSync(
|
||||
path.resolve(paths.cast_output_root, "media.html"),
|
||||
contentMedia
|
||||
);
|
||||
|
||||
const contentFAQ = renderCastTemplate("launcher-faq", {
|
||||
latestLauncherJS: latestManifest["launcher.js"],
|
||||
es5LauncherJS: es5Manifest["launcher.js"],
|
||||
@@ -320,23 +302,15 @@ gulp.task("gen-index-hassio-prod", async () => {
|
||||
|
||||
function writeHassioEntrypoint(latestEntrypoint, es5Entrypoint) {
|
||||
fs.mkdirSync(paths.hassio_output_root, { recursive: true });
|
||||
// Safari 12 and below does not have a compliant ES2015 implementation of template literals, so we ship ES5
|
||||
fs.writeFileSync(
|
||||
path.resolve(paths.hassio_output_root, "entrypoint.js"),
|
||||
`
|
||||
function loadES5() {
|
||||
try {
|
||||
new Function("import('${latestEntrypoint}')")();
|
||||
} catch (err) {
|
||||
var el = document.createElement('script');
|
||||
el.src = '${es5Entrypoint}';
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
if (/.*Version\\/(?:11|12)(?:\\.\\d+)*.*Safari\\//.test(navigator.userAgent)) {
|
||||
loadES5();
|
||||
} else {
|
||||
try {
|
||||
new Function("import('${latestEntrypoint}')")();
|
||||
} catch (err) {
|
||||
loadES5();
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ encoding: "utf-8" }
|
||||
|
@@ -1,174 +0,0 @@
|
||||
// Task to download the latest Lokalise translations from the nightly workflow artifacts
|
||||
|
||||
const del = import("del");
|
||||
const fs = require("fs/promises");
|
||||
const path = require("path");
|
||||
const process = require("process");
|
||||
const gulp = require("gulp");
|
||||
const jszip = require("jszip");
|
||||
const tar = require("tar");
|
||||
const { Octokit } = require("@octokit/rest");
|
||||
const { createOAuthDeviceAuth } = require("@octokit/auth-oauth-device");
|
||||
|
||||
const MAX_AGE = 24; // hours
|
||||
const OWNER = "home-assistant";
|
||||
const REPO = "frontend";
|
||||
const WORKFLOW_NAME = "nightly.yaml";
|
||||
const ARTIFACT_NAME = "translations";
|
||||
const CLIENT_ID = "Iv1.3914e28cb27834d1";
|
||||
const EXTRACT_DIR = "translations";
|
||||
const TOKEN_FILE = path.posix.join(EXTRACT_DIR, "token.json");
|
||||
const ARTIFACT_FILE = path.posix.join(EXTRACT_DIR, "artifact.json");
|
||||
|
||||
let allowTokenSetup = false;
|
||||
gulp.task("allow-setup-fetch-nightly-translations", (done) => {
|
||||
allowTokenSetup = true;
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("fetch-nightly-translations", async function () {
|
||||
// Skip all when environment flag is set (assumes translations are already in place)
|
||||
if (process.env?.SKIP_FETCH_NIGHTLY_TRANSLATIONS) {
|
||||
console.log("Skipping fetch due to environment signal");
|
||||
return;
|
||||
}
|
||||
|
||||
// Read current translations artifact info if it exists,
|
||||
// and stop if they are not old enough
|
||||
let currentArtifact;
|
||||
try {
|
||||
currentArtifact = JSON.parse(await fs.readFile(ARTIFACT_FILE, "utf-8"));
|
||||
const currentAge =
|
||||
(Date.now() - Date.parse(currentArtifact.created_at)) / 3600000;
|
||||
if (currentAge < MAX_AGE) {
|
||||
console.log(
|
||||
"Keeping current translations (only %s hours old)",
|
||||
currentAge.toFixed(1)
|
||||
);
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
currentArtifact = null;
|
||||
}
|
||||
|
||||
// To store file writing promises
|
||||
const createExtractDir = fs.mkdir(EXTRACT_DIR, { recursive: true });
|
||||
const writings = [];
|
||||
|
||||
// Authenticate to GitHub using GitHub action token if it exists,
|
||||
// otherwise look for a saved user token or generate a new one if none
|
||||
let tokenAuth;
|
||||
if (process.env.GITHUB_TOKEN) {
|
||||
tokenAuth = { token: process.env.GITHUB_TOKEN };
|
||||
} else {
|
||||
try {
|
||||
tokenAuth = JSON.parse(await fs.readFile(TOKEN_FILE, "utf-8"));
|
||||
} catch {
|
||||
if (!allowTokenSetup) {
|
||||
console.log("No token found so build wil continue with English only");
|
||||
return;
|
||||
}
|
||||
const auth = createOAuthDeviceAuth({
|
||||
clientType: "github-app",
|
||||
clientId: CLIENT_ID,
|
||||
onVerification: (verification) => {
|
||||
console.log(
|
||||
"Task needs to authenticate to GitHub to fetch the translations from nightly workflow\n" +
|
||||
"Please go to %s to authorize this task\n" +
|
||||
"\nEnter user code: %s\n\n" +
|
||||
"This code will expire in %s minutes\n" +
|
||||
"Task will automatically continue after authorization and token will be saved for future use",
|
||||
verification.verification_uri,
|
||||
verification.user_code,
|
||||
(verification.expires_in / 60).toFixed(0)
|
||||
);
|
||||
},
|
||||
});
|
||||
tokenAuth = await auth({ type: "oauth" });
|
||||
writings.push(
|
||||
createExtractDir.then(
|
||||
fs.writeFile(TOKEN_FILE, JSON.stringify(tokenAuth, null, 2))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Authenticate with token and request workflow runs from GitHub
|
||||
console.log("Fetching new translations...");
|
||||
const octokit = new Octokit({
|
||||
userAgent: "Fetch Nightly Translations",
|
||||
auth: tokenAuth.token,
|
||||
});
|
||||
|
||||
const workflowRunsResponse = await octokit.rest.actions.listWorkflowRuns({
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
workflow_id: WORKFLOW_NAME,
|
||||
status: "success",
|
||||
event: "schedule",
|
||||
per_page: 1,
|
||||
exclude_pull_requests: true,
|
||||
});
|
||||
if (workflowRunsResponse.data.total_count === 0) {
|
||||
throw Error("No successful nightly workflow runs found");
|
||||
}
|
||||
const latestNightlyRun = workflowRunsResponse.data.workflow_runs[0];
|
||||
|
||||
// Stop if current is already the latest, otherwise Find the translations artifact
|
||||
if (currentArtifact?.workflow_run.id === latestNightlyRun.id) {
|
||||
console.log("Stopping because current translations are still the latest");
|
||||
return;
|
||||
}
|
||||
const latestArtifact = (
|
||||
await octokit.actions.listWorkflowRunArtifacts({
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
run_id: latestNightlyRun.id,
|
||||
})
|
||||
).data.artifacts.find((artifact) => artifact.name === ARTIFACT_NAME);
|
||||
if (!latestArtifact) {
|
||||
throw Error("Latest nightly workflow run has no translations artifact");
|
||||
}
|
||||
writings.push(
|
||||
createExtractDir.then(
|
||||
fs.writeFile(ARTIFACT_FILE, JSON.stringify(latestArtifact, null, 2))
|
||||
)
|
||||
);
|
||||
|
||||
// Remove the current translations
|
||||
const deleteCurrent = Promise.all(writings).then(
|
||||
(await del).deleteAsync([
|
||||
`${EXTRACT_DIR}/*`,
|
||||
`!${ARTIFACT_FILE}`,
|
||||
`!${TOKEN_FILE}`,
|
||||
])
|
||||
);
|
||||
|
||||
// Get the download URL and follow the redirect to download (stored as ArrayBuffer)
|
||||
const downloadResponse = await octokit.actions.downloadArtifact({
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
artifact_id: latestArtifact.id,
|
||||
archive_format: "zip",
|
||||
});
|
||||
if (downloadResponse.status !== 200) {
|
||||
throw Error("Failure downloading translations artifact");
|
||||
}
|
||||
|
||||
// Artifact is a tarball, but GitHub adds it to a zip file
|
||||
console.log("Unpacking downloaded translations...");
|
||||
const zip = await jszip.loadAsync(downloadResponse.data);
|
||||
await deleteCurrent;
|
||||
const extractStream = zip.file(/.*/)[0].nodeStream().pipe(tar.extract());
|
||||
await new Promise((resolve, reject) => {
|
||||
extractStream.on("close", resolve).on("error", reject);
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
"setup-and-fetch-nightly-translations",
|
||||
gulp.series(
|
||||
"allow-setup-fetch-nightly-translations",
|
||||
"fetch-nightly-translations"
|
||||
)
|
||||
);
|
@@ -2,9 +2,6 @@
|
||||
const gulp = require("gulp");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { marked } = require("marked");
|
||||
const glob = require("glob");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const env = require("../env");
|
||||
const paths = require("../paths");
|
||||
@@ -18,129 +15,26 @@ require("./service-worker.js");
|
||||
require("./entry-html.js");
|
||||
require("./rollup.js");
|
||||
|
||||
gulp.task("gather-gallery-pages", async function gatherPages() {
|
||||
const pageDir = path.resolve(paths.gallery_dir, "src/pages");
|
||||
const files = await glob(path.resolve(pageDir, "**/*"));
|
||||
gulp.task("gather-gallery-demos", async function gatherDemos() {
|
||||
const files = await fs.promises.readdir(
|
||||
path.resolve(paths.gallery_dir, "src/demos")
|
||||
);
|
||||
|
||||
const galleryBuild = path.resolve(paths.gallery_dir, "build");
|
||||
fs.mkdirSync(galleryBuild, { recursive: true });
|
||||
|
||||
let content = "export const PAGES = {\n";
|
||||
|
||||
const processed = new Set();
|
||||
let content = "export const DEMOS = {\n";
|
||||
|
||||
for (const file of files) {
|
||||
if (fs.lstatSync(file).isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
const pageId = file.substring(pageDir.length + 1, file.lastIndexOf("."));
|
||||
|
||||
if (processed.has(pageId)) {
|
||||
continue;
|
||||
}
|
||||
processed.add(pageId);
|
||||
|
||||
const [category] = pageId.split("/", 2);
|
||||
|
||||
const demoFile = path.resolve(pageDir, `${pageId}.ts`);
|
||||
const descriptionFile = path.resolve(pageDir, `${pageId}.markdown`);
|
||||
const hasDemo = fs.existsSync(demoFile);
|
||||
let hasDescription = fs.existsSync(descriptionFile);
|
||||
let metadata = {};
|
||||
if (hasDescription) {
|
||||
let descriptionContent = fs.readFileSync(descriptionFile, "utf-8");
|
||||
|
||||
if (descriptionContent.startsWith("---")) {
|
||||
const metadataEnd = descriptionContent.indexOf("---", 3);
|
||||
metadata = yaml.load(descriptionContent.substring(3, metadataEnd));
|
||||
descriptionContent = descriptionContent
|
||||
.substring(metadataEnd + 3)
|
||||
.trim();
|
||||
}
|
||||
|
||||
// If description is just metadata
|
||||
if (descriptionContent === "") {
|
||||
hasDescription = false;
|
||||
} else {
|
||||
descriptionContent = marked(descriptionContent).replace(/`/g, "\\`");
|
||||
fs.mkdirSync(path.resolve(galleryBuild, category), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.resolve(galleryBuild, `${pageId}-description.ts`),
|
||||
`
|
||||
import {html} from "lit";
|
||||
export default html\`${descriptionContent}\`
|
||||
`
|
||||
);
|
||||
}
|
||||
}
|
||||
content += ` "${pageId}": {
|
||||
metadata: ${JSON.stringify(metadata)},
|
||||
${
|
||||
hasDescription
|
||||
? `description: () => import("./${pageId}-description").then(m => m.default),`
|
||||
: ""
|
||||
}
|
||||
${hasDemo ? `demo: () => import("../src/pages/${pageId}")` : ""}
|
||||
|
||||
},\n`;
|
||||
const demoId = path.basename(file, ".ts");
|
||||
const demoPath = "../src/demos/" + demoId;
|
||||
content += ` "${demoId}": () => import("${demoPath}"),\n`;
|
||||
}
|
||||
|
||||
content += "};\n";
|
||||
content += "};";
|
||||
|
||||
// Generate sidebar
|
||||
const sidebarPath = path.resolve(paths.gallery_dir, "sidebar.js");
|
||||
// To make watch work during development
|
||||
delete require.cache[sidebarPath];
|
||||
const sidebar = require(sidebarPath);
|
||||
|
||||
const pagesToProcess = {};
|
||||
for (const key of processed) {
|
||||
const [category, page] = key.split("/", 2);
|
||||
if (!(category in pagesToProcess)) {
|
||||
pagesToProcess[category] = new Set();
|
||||
}
|
||||
pagesToProcess[category].add(page);
|
||||
}
|
||||
|
||||
for (const group of Object.values(sidebar)) {
|
||||
const toProcess = pagesToProcess[group.category];
|
||||
delete pagesToProcess[group.category];
|
||||
|
||||
if (!toProcess) {
|
||||
console.error("Unknown category", group.category);
|
||||
if (!group.pages) {
|
||||
group.pages = [];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Any pre-defined groups will not be sorted.
|
||||
if (group.pages) {
|
||||
for (const page of group.pages) {
|
||||
if (!toProcess.delete(page)) {
|
||||
console.error("Found unreferenced demo", page);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
group.pages = [];
|
||||
}
|
||||
for (const page of Array.from(toProcess).sort()) {
|
||||
group.pages.push(page);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [category, pages] of Object.entries(pagesToProcess)) {
|
||||
sidebar.push({
|
||||
category,
|
||||
header: category,
|
||||
pages: Array.from(pages).sort(),
|
||||
});
|
||||
}
|
||||
|
||||
content += `export const SIDEBAR = ${JSON.stringify(sidebar, null, 2)};\n`;
|
||||
const galleryBuild = path.resolve(paths.gallery_dir, "build");
|
||||
|
||||
fs.mkdirSync(galleryBuild, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.resolve(galleryBuild, "import-pages.ts"),
|
||||
path.resolve(galleryBuild, "import-demos.ts"),
|
||||
content,
|
||||
"utf-8"
|
||||
);
|
||||
@@ -157,25 +51,11 @@ gulp.task(
|
||||
gulp.parallel(
|
||||
"gen-icons-json",
|
||||
"build-translations",
|
||||
"build-locale-data",
|
||||
"gather-gallery-pages"
|
||||
"gather-gallery-demos"
|
||||
),
|
||||
"copy-static-gallery",
|
||||
"gen-index-gallery-dev",
|
||||
gulp.parallel(
|
||||
env.useRollup()
|
||||
? "rollup-dev-server-gallery"
|
||||
: "webpack-dev-server-gallery",
|
||||
async function watchMarkdownFiles() {
|
||||
gulp.watch(
|
||||
[
|
||||
path.resolve(paths.gallery_dir, "src/pages/**/*.markdown"),
|
||||
path.resolve(paths.gallery_dir, "sidebar.js"),
|
||||
],
|
||||
gulp.series("gather-gallery-pages")
|
||||
);
|
||||
}
|
||||
)
|
||||
env.useRollup() ? "rollup-dev-server-gallery" : "webpack-dev-server-gallery"
|
||||
)
|
||||
);
|
||||
|
||||
@@ -190,8 +70,7 @@ gulp.task(
|
||||
gulp.parallel(
|
||||
"gen-icons-json",
|
||||
"build-translations",
|
||||
"build-locale-data",
|
||||
"gather-gallery-pages"
|
||||
"gather-gallery-demos"
|
||||
),
|
||||
"copy-static-gallery",
|
||||
env.useRollup() ? "rollup-prod-gallery" : "webpack-prod-gallery",
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
const gulp = require("gulp");
|
||||
const path = require("path");
|
||||
const cpx = require("cpx");
|
||||
const fs = require("fs-extra");
|
||||
const paths = require("../paths");
|
||||
|
||||
@@ -12,28 +13,19 @@ const polyPath = (...parts) => path.resolve(paths.polymer_dir, ...parts);
|
||||
const copyFileDir = (fromFile, toDir) =>
|
||||
fs.copySync(fromFile, path.join(toDir, path.basename(fromFile)));
|
||||
|
||||
const genStaticPath =
|
||||
(staticDir) =>
|
||||
(...parts) =>
|
||||
path.resolve(staticDir, ...parts);
|
||||
const genStaticPath = (staticDir) => (...parts) =>
|
||||
path.resolve(staticDir, ...parts);
|
||||
|
||||
function copyTranslations(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
|
||||
// Translation output
|
||||
fs.copySync(
|
||||
polyPath("build/translations/output"),
|
||||
polyPath("build-translations/output"),
|
||||
staticPath("translations")
|
||||
);
|
||||
}
|
||||
|
||||
function copyLocaleData(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
|
||||
// Locale data output
|
||||
fs.copySync(polyPath("build/locale-data"), staticPath("locale-data"));
|
||||
}
|
||||
|
||||
function copyMdiIcons(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
|
||||
@@ -70,20 +62,12 @@ function copyLoaderJS(staticDir) {
|
||||
function copyFonts(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
// Local fonts
|
||||
fs.copySync(
|
||||
npmPath("roboto-fontface/fonts/roboto/"),
|
||||
staticPath("fonts/roboto/"),
|
||||
{
|
||||
filter: (src) => !src.includes(".") || src.endsWith(".woff2"),
|
||||
}
|
||||
cpx.copySync(
|
||||
npmPath("roboto-fontface/fonts/roboto/*.woff2"),
|
||||
staticPath("fonts/roboto")
|
||||
);
|
||||
}
|
||||
|
||||
function copyQrScannerWorker(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
copyFileDir(npmPath("qr-scanner/qr-scanner-worker.min.js"), staticPath("js"));
|
||||
}
|
||||
|
||||
function copyMapPanel(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
copyFileDir(
|
||||
@@ -96,26 +80,11 @@ function copyMapPanel(staticDir) {
|
||||
);
|
||||
}
|
||||
|
||||
gulp.task("copy-locale-data", async () => {
|
||||
const staticDir = paths.app_output_static;
|
||||
copyLocaleData(staticDir);
|
||||
});
|
||||
|
||||
gulp.task("copy-translations-app", async () => {
|
||||
const staticDir = paths.app_output_static;
|
||||
copyTranslations(staticDir);
|
||||
});
|
||||
|
||||
gulp.task("copy-translations-supervisor", async () => {
|
||||
const staticDir = paths.hassio_output_static;
|
||||
copyTranslations(staticDir);
|
||||
});
|
||||
|
||||
gulp.task("copy-locale-data-supervisor", async () => {
|
||||
const staticDir = paths.hassio_output_static;
|
||||
copyLocaleData(staticDir);
|
||||
});
|
||||
|
||||
gulp.task("copy-static-app", async () => {
|
||||
const staticDir = paths.app_output_static;
|
||||
// Basic static files
|
||||
@@ -125,14 +94,10 @@ gulp.task("copy-static-app", async () => {
|
||||
copyPolyfills(staticDir);
|
||||
copyFonts(staticDir);
|
||||
copyTranslations(staticDir);
|
||||
copyLocaleData(staticDir);
|
||||
copyMdiIcons(staticDir);
|
||||
|
||||
// Panel assets
|
||||
copyMapPanel(staticDir);
|
||||
|
||||
// Qr Scanner assets
|
||||
copyQrScannerWorker(staticDir);
|
||||
});
|
||||
|
||||
gulp.task("copy-static-demo", async () => {
|
||||
@@ -149,7 +114,6 @@ gulp.task("copy-static-demo", async () => {
|
||||
copyMapPanel(paths.demo_output_static);
|
||||
copyFonts(paths.demo_output_static);
|
||||
copyTranslations(paths.demo_output_static);
|
||||
copyLocaleData(paths.demo_output_static);
|
||||
copyMdiIcons(paths.demo_output_static);
|
||||
});
|
||||
|
||||
@@ -164,7 +128,6 @@ gulp.task("copy-static-cast", async () => {
|
||||
copyMapPanel(paths.cast_output_static);
|
||||
copyFonts(paths.cast_output_static);
|
||||
copyTranslations(paths.cast_output_static);
|
||||
copyLocaleData(paths.cast_output_static);
|
||||
copyMdiIcons(paths.cast_output_static);
|
||||
});
|
||||
|
||||
@@ -180,6 +143,5 @@ gulp.task("copy-static-gallery", async () => {
|
||||
copyMapPanel(paths.gallery_output_static);
|
||||
copyFonts(paths.gallery_output_static);
|
||||
copyTranslations(paths.gallery_output_static);
|
||||
copyLocaleData(paths.gallery_output_static);
|
||||
copyMdiIcons(paths.gallery_output_static);
|
||||
});
|
||||
|
@@ -22,40 +22,17 @@ const getMeta = () => {
|
||||
const svg = fs.readFileSync(`${ICON_PATH}/${icon.name}.svg`, {
|
||||
encoding,
|
||||
});
|
||||
return {
|
||||
path: svg.match(/ d="([^"]+)"/)[1],
|
||||
name: icon.name,
|
||||
tags: icon.tags,
|
||||
aliases: icon.aliases,
|
||||
};
|
||||
return { path: svg.match(/ d="([^"]+)"/)[1], name: icon.name };
|
||||
});
|
||||
};
|
||||
|
||||
const addRemovedMeta = (meta) => {
|
||||
const file = fs.readFileSync(REMOVED_ICONS_PATH, { encoding });
|
||||
const removed = JSON.parse(file);
|
||||
const removedMeta = removed.map((removeIcon) => ({
|
||||
path: removeIcon.path,
|
||||
name: removeIcon.name,
|
||||
tags: [],
|
||||
aliases: [],
|
||||
}));
|
||||
const combinedMeta = [...meta, ...removedMeta];
|
||||
const combinedMeta = [...meta, ...removed];
|
||||
return combinedMeta.sort((a, b) => a.name.localeCompare(b.name));
|
||||
};
|
||||
|
||||
const homeAutomationTag = "Home Automation";
|
||||
|
||||
const orderMeta = (meta) => {
|
||||
const homeAutomationMeta = meta.filter((icon) =>
|
||||
icon.tags.includes(homeAutomationTag)
|
||||
);
|
||||
const otherMeta = meta.filter(
|
||||
(icon) => !icon.tags.includes(homeAutomationTag)
|
||||
);
|
||||
return [...homeAutomationMeta, ...otherMeta];
|
||||
};
|
||||
|
||||
const splitBySize = (meta) => {
|
||||
const chunks = [];
|
||||
const CHUNK_SIZE = 50000;
|
||||
@@ -100,10 +77,8 @@ const findDifferentiator = (curString, prevString) => {
|
||||
};
|
||||
|
||||
gulp.task("gen-icons-json", (done) => {
|
||||
const meta = getMeta();
|
||||
|
||||
const metaAndRemoved = addRemovedMeta(meta);
|
||||
const split = splitBySize(metaAndRemoved);
|
||||
const meta = addRemovedMeta(getMeta());
|
||||
const split = splitBySize(meta);
|
||||
|
||||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
@@ -141,27 +116,5 @@ gulp.task("gen-icons-json", (done) => {
|
||||
JSON.stringify({ version: package.version, parts })
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.resolve(OUTPUT_DIR, "iconList.json"),
|
||||
JSON.stringify(
|
||||
orderMeta(meta).map((icon) => ({
|
||||
name: icon.name,
|
||||
keywords: [
|
||||
...icon.tags.map((t) => t.toLowerCase().replace(/\s\/\s/g, " ")),
|
||||
...icon.aliases,
|
||||
],
|
||||
}))
|
||||
)
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("gen-dummy-icons-json", (done) => {
|
||||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
fs.writeFileSync(path.resolve(OUTPUT_DIR, "iconList.json"), "[]");
|
||||
done();
|
||||
});
|
||||
|
@@ -1,13 +1,15 @@
|
||||
const gulp = require("gulp");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const env = require("../env");
|
||||
const paths = require("../paths");
|
||||
|
||||
require("./clean.js");
|
||||
require("./gen-icons-json.js");
|
||||
require("./webpack.js");
|
||||
require("./compress.js");
|
||||
require("./rollup.js");
|
||||
require("./gather-static.js");
|
||||
require("./translations.js");
|
||||
require("./gen-icons-json.js");
|
||||
|
||||
gulp.task(
|
||||
"develop-hassio",
|
||||
@@ -16,12 +18,8 @@ gulp.task(
|
||||
process.env.NODE_ENV = "development";
|
||||
},
|
||||
"clean-hassio",
|
||||
"gen-dummy-icons-json",
|
||||
"gen-icons-json",
|
||||
"gen-index-hassio-dev",
|
||||
"build-supervisor-translations",
|
||||
"copy-translations-supervisor",
|
||||
"build-locale-data",
|
||||
"copy-locale-data-supervisor",
|
||||
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
|
||||
)
|
||||
);
|
||||
@@ -33,14 +31,10 @@ gulp.task(
|
||||
process.env.NODE_ENV = "production";
|
||||
},
|
||||
"clean-hassio",
|
||||
"gen-dummy-icons-json",
|
||||
"build-supervisor-translations",
|
||||
"copy-translations-supervisor",
|
||||
"build-locale-data",
|
||||
"copy-locale-data-supervisor",
|
||||
"gen-icons-json",
|
||||
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
|
||||
"gen-index-hassio-prod",
|
||||
...// Don't compress running tests
|
||||
(env.isTestBuild() ? [] : ["compress-hassio"])
|
||||
(env.isTest() ? [] : ["compress-hassio"])
|
||||
)
|
||||
);
|
||||
|
@@ -1,72 +0,0 @@
|
||||
const del = import("del");
|
||||
const path = require("path");
|
||||
const gulp = require("gulp");
|
||||
const fs = require("fs");
|
||||
const paths = require("../paths");
|
||||
|
||||
const outDir = "build/locale-data";
|
||||
|
||||
gulp.task("clean-locale-data", async () => (await del).deleteSync([outDir]));
|
||||
|
||||
gulp.task("ensure-locale-data-build-dir", (done) => {
|
||||
if (!fs.existsSync(outDir)) {
|
||||
fs.mkdirSync(outDir, { recursive: true });
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
const modules = {
|
||||
"intl-relativetimeformat": "RelativeTimeFormat",
|
||||
"intl-datetimeformat": "DateTimeFormat",
|
||||
"intl-numberformat": "NumberFormat",
|
||||
};
|
||||
|
||||
gulp.task("create-locale-data", (done) => {
|
||||
const translationMeta = JSON.parse(
|
||||
fs.readFileSync(
|
||||
path.join(paths.translations_src, "translationMetadata.json")
|
||||
)
|
||||
);
|
||||
Object.entries(modules).forEach(([module, className]) => {
|
||||
Object.keys(translationMeta).forEach((lang) => {
|
||||
try {
|
||||
const localeData = String(
|
||||
fs.readFileSync(
|
||||
require.resolve(`@formatjs/${module}/locale-data/${lang}.js`)
|
||||
)
|
||||
)
|
||||
.replace(
|
||||
new RegExp(
|
||||
`\\/\\*\\s*@generated\\s*\\*\\/\\s*\\/\\/\\s*prettier-ignore\\s*if\\s*\\(Intl\\.${className}\\s*&&\\s*typeof\\s*Intl\\.${className}\\.__addLocaleData\\s*===\\s*'function'\\)\\s*{\\s*Intl\\.${className}\\.__addLocaleData\\(`,
|
||||
"im"
|
||||
),
|
||||
""
|
||||
)
|
||||
.replace(/\)\s*}/im, "");
|
||||
// make sure we have valid JSON
|
||||
JSON.parse(localeData);
|
||||
if (!fs.existsSync(path.join(outDir, module))) {
|
||||
fs.mkdirSync(path.join(outDir, module), { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(
|
||||
path.join(outDir, `${module}/${lang}.json`),
|
||||
localeData
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.code !== "MODULE_NOT_FOUND") {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
"build-locale-data",
|
||||
gulp.series(
|
||||
"clean-locale-data",
|
||||
"ensure-locale-data-build-dir",
|
||||
"create-locale-data"
|
||||
)
|
||||
);
|
@@ -5,9 +5,9 @@ const rollup = require("rollup");
|
||||
const handler = require("serve-handler");
|
||||
const http = require("http");
|
||||
const log = require("fancy-log");
|
||||
const open = require("open");
|
||||
const rollupConfig = require("../rollup");
|
||||
const paths = require("../paths");
|
||||
const open = require("open");
|
||||
|
||||
const bothBuilds = (createConfigFunc, params) =>
|
||||
gulp.series(
|
||||
@@ -30,11 +30,11 @@ const bothBuilds = (createConfigFunc, params) =>
|
||||
);
|
||||
|
||||
function createServer(serveOptions) {
|
||||
const server = http.createServer((request, response) =>
|
||||
handler(request, response, {
|
||||
const server = http.createServer((request, response) => {
|
||||
return handler(request, response, {
|
||||
public: serveOptions.root,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(
|
||||
serveOptions.port,
|
||||
|
@@ -1,5 +1,7 @@
|
||||
// Generate service worker.
|
||||
// Based on manifest, create a file with the content as service_worker.js
|
||||
/* eslint-disable import/no-dynamic-require */
|
||||
/* eslint-disable global-require */
|
||||
const gulp = require("gulp");
|
||||
const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
|
@@ -1,11 +1,11 @@
|
||||
const del = import("del");
|
||||
const crypto = require("crypto");
|
||||
const del = require("del");
|
||||
const path = require("path");
|
||||
const source = require("vinyl-source-stream");
|
||||
const vinylBuffer = require("vinyl-buffer");
|
||||
const gulp = require("gulp");
|
||||
const fs = require("fs");
|
||||
const flatmap = require("gulp-flatmap");
|
||||
const foreach = require("gulp-foreach");
|
||||
const merge = require("gulp-merge-json");
|
||||
const rename = require("gulp-rename");
|
||||
const transform = require("gulp-json-transform");
|
||||
@@ -13,23 +13,25 @@ const { mapFiles } = require("../util");
|
||||
const env = require("../env");
|
||||
const paths = require("../paths");
|
||||
|
||||
require("./fetch-nightly-translations");
|
||||
|
||||
const inFrontendDir = "translations/frontend";
|
||||
const inBackendDir = "translations/backend";
|
||||
const workDir = "build/translations";
|
||||
const workDir = "build-translations";
|
||||
const fullDir = workDir + "/full";
|
||||
const coreDir = workDir + "/core";
|
||||
const outDir = workDir + "/output";
|
||||
let mergeBackend = false;
|
||||
|
||||
gulp.task(
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel((done) => {
|
||||
mergeBackend = true;
|
||||
done();
|
||||
}, "allow-setup-fetch-nightly-translations")
|
||||
);
|
||||
gulp.task("translations-enable-merge-backend", (done) => {
|
||||
mergeBackend = true;
|
||||
done();
|
||||
});
|
||||
|
||||
String.prototype.rsplit = function (sep, maxsplit) {
|
||||
var split = this.split(sep);
|
||||
return maxsplit
|
||||
? [split.slice(0, -maxsplit).join(sep)].concat(split.slice(-maxsplit))
|
||||
: split;
|
||||
};
|
||||
|
||||
// Panel translations which should be split from the core translations.
|
||||
const TRANSLATION_FRAGMENTS = Object.keys(
|
||||
@@ -38,7 +40,7 @@ const TRANSLATION_FRAGMENTS = Object.keys(
|
||||
|
||||
function recursiveFlatten(prefix, data) {
|
||||
let output = {};
|
||||
Object.keys(data).forEach((key) => {
|
||||
Object.keys(data).forEach(function (key) {
|
||||
if (typeof data[key] === "object") {
|
||||
output = {
|
||||
...output,
|
||||
@@ -99,19 +101,15 @@ function lokaliseTransform(data, original, file) {
|
||||
if (value instanceof Object) {
|
||||
output[key] = lokaliseTransform(value, original, file);
|
||||
} else {
|
||||
output[key] = value.replace(re_key_reference, (_match, lokalise_key) => {
|
||||
const replace = lokalise_key.split("::").reduce((tr, k) => {
|
||||
output[key] = value.replace(re_key_reference, (match, key) => {
|
||||
const replace = key.split("::").reduce((tr, k) => {
|
||||
if (!tr) {
|
||||
throw Error(
|
||||
`Invalid key placeholder ${lokalise_key} in ${file.path}`
|
||||
);
|
||||
throw Error(`Invalid key placeholder ${key} in ${file.path}`);
|
||||
}
|
||||
return tr[k];
|
||||
}, original);
|
||||
if (typeof replace !== "string") {
|
||||
throw Error(
|
||||
`Invalid key placeholder ${lokalise_key} in ${file.path}`
|
||||
);
|
||||
throw Error(`Invalid key placeholder ${key} in ${file.path}`);
|
||||
}
|
||||
return replace;
|
||||
});
|
||||
@@ -120,16 +118,18 @@ function lokaliseTransform(data, original, file) {
|
||||
return output;
|
||||
}
|
||||
|
||||
gulp.task("clean-translations", async () => (await del).deleteSync([workDir]));
|
||||
gulp.task("clean-translations", function () {
|
||||
return del([workDir]);
|
||||
});
|
||||
|
||||
gulp.task("ensure-translations-build-dir", (done) => {
|
||||
if (!fs.existsSync(workDir)) {
|
||||
fs.mkdirSync(workDir, { recursive: true });
|
||||
fs.mkdirSync(workDir);
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("create-test-metadata", (cb) => {
|
||||
gulp.task("create-test-metadata", function (cb) {
|
||||
fs.writeFile(
|
||||
workDir + "/testMetadata.json",
|
||||
JSON.stringify({
|
||||
@@ -143,13 +143,17 @@ gulp.task("create-test-metadata", (cb) => {
|
||||
|
||||
gulp.task(
|
||||
"create-test-translation",
|
||||
gulp.series("create-test-metadata", () =>
|
||||
gulp
|
||||
gulp.series("create-test-metadata", function createTestTranslation() {
|
||||
return gulp
|
||||
.src(path.join(paths.translations_src, "en.json"))
|
||||
.pipe(transform((data, _file) => recursiveEmpty(data)))
|
||||
.pipe(
|
||||
transform(function (data, file) {
|
||||
return recursiveEmpty(data);
|
||||
})
|
||||
)
|
||||
.pipe(rename("test.json"))
|
||||
.pipe(gulp.dest(workDir))
|
||||
)
|
||||
.pipe(gulp.dest(workDir));
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -161,7 +165,7 @@ gulp.task(
|
||||
* project is buildable immediately after merging new translation keys, since
|
||||
* the Lokalise update to translations/en.json will not happen immediately.
|
||||
*/
|
||||
gulp.task("build-master-translation", () => {
|
||||
gulp.task("build-master-translation", function () {
|
||||
const src = [path.join(paths.translations_src, "en.json")];
|
||||
|
||||
if (mergeBackend) {
|
||||
@@ -170,30 +174,31 @@ gulp.task("build-master-translation", () => {
|
||||
|
||||
return gulp
|
||||
.src(src)
|
||||
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||
.pipe(
|
||||
merge({
|
||||
fileName: "en.json",
|
||||
transform(function (data, file) {
|
||||
return lokaliseTransform(data, data, file);
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(fullDir));
|
||||
.pipe(
|
||||
merge({
|
||||
fileName: "translationMaster.json",
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(workDir));
|
||||
});
|
||||
|
||||
gulp.task("build-merged-translations", () =>
|
||||
gulp
|
||||
.src(
|
||||
[
|
||||
inFrontendDir + "/*.json",
|
||||
"!" + inFrontendDir + "/en.json",
|
||||
workDir + "/test.json",
|
||||
],
|
||||
{
|
||||
allowEmpty: true,
|
||||
}
|
||||
)
|
||||
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||
gulp.task("build-merged-translations", function () {
|
||||
return gulp
|
||||
.src([inFrontendDir + "/*.json", workDir + "/test.json"], {
|
||||
allowEmpty: true,
|
||||
})
|
||||
.pipe(
|
||||
flatmap((stream, file) => {
|
||||
transform(function (data, file) {
|
||||
return lokaliseTransform(data, data, file);
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
foreach(function (stream, file) {
|
||||
// For each language generate a merged json file. It begins with the master
|
||||
// translation as a failsafe for untranslated strings, and merges all parent
|
||||
// tags into one file for each specific subtag
|
||||
@@ -203,7 +208,7 @@ gulp.task("build-merged-translations", () =>
|
||||
// than a base translation + region.
|
||||
const tr = path.basename(file.history[0], ".json");
|
||||
const subtags = tr.split("-");
|
||||
const src = [fullDir + "/en.json"];
|
||||
const src = [workDir + "/translationMaster.json"];
|
||||
for (let i = 1; i <= subtags.length; i++) {
|
||||
const lang = subtags.slice(0, i).join("-");
|
||||
if (lang === "test") {
|
||||
@@ -225,17 +230,17 @@ gulp.task("build-merged-translations", () =>
|
||||
)
|
||||
.pipe(gulp.dest(fullDir));
|
||||
})
|
||||
)
|
||||
);
|
||||
);
|
||||
});
|
||||
|
||||
let taskName;
|
||||
var taskName;
|
||||
|
||||
const splitTasks = [];
|
||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
taskName = "build-translation-fragment-" + fragment;
|
||||
gulp.task(taskName, () =>
|
||||
gulp.task(taskName, function () {
|
||||
// Return only the translations for this fragment.
|
||||
gulp
|
||||
return gulp
|
||||
.src(fullDir + "/*.json")
|
||||
.pipe(
|
||||
transform((data) => ({
|
||||
@@ -246,33 +251,32 @@ TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
},
|
||||
}))
|
||||
)
|
||||
.pipe(gulp.dest(workDir + "/" + fragment))
|
||||
);
|
||||
.pipe(gulp.dest(workDir + "/" + fragment));
|
||||
});
|
||||
splitTasks.push(taskName);
|
||||
});
|
||||
|
||||
taskName = "build-translation-core";
|
||||
gulp.task(taskName, () =>
|
||||
gulp.task(taskName, function () {
|
||||
// Remove the fragment translations from the core translation.
|
||||
gulp
|
||||
return gulp
|
||||
.src(fullDir + "/*.json")
|
||||
.pipe(
|
||||
transform((data, _file) => {
|
||||
transform((data, file) => {
|
||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
delete data.ui.panel[fragment];
|
||||
});
|
||||
delete data.supervisor;
|
||||
return data;
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(coreDir))
|
||||
);
|
||||
.pipe(gulp.dest(coreDir));
|
||||
});
|
||||
|
||||
splitTasks.push(taskName);
|
||||
|
||||
gulp.task("build-flattened-translations", () =>
|
||||
gulp.task("build-flattened-translations", function () {
|
||||
// Flatten the split versions of our translations, and move them into outDir
|
||||
gulp
|
||||
return gulp
|
||||
.src(
|
||||
TRANSLATION_FRAGMENTS.map(
|
||||
(fragment) => workDir + "/" + fragment + "/*.json"
|
||||
@@ -280,45 +284,41 @@ gulp.task("build-flattened-translations", () =>
|
||||
{ base: workDir }
|
||||
)
|
||||
.pipe(
|
||||
transform((data) =>
|
||||
transform(function (data) {
|
||||
// Polymer.AppLocalizeBehavior requires flattened json
|
||||
flatten(data)
|
||||
)
|
||||
return flatten(data);
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
rename((filePath) => {
|
||||
if (filePath.dirname === "core") {
|
||||
filePath.dirname = "";
|
||||
}
|
||||
// In dev we create the file with the fake hash in the filename
|
||||
if (!env.isProdBuild()) {
|
||||
filePath.basename += "-dev";
|
||||
}
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(outDir))
|
||||
);
|
||||
.pipe(gulp.dest(outDir));
|
||||
});
|
||||
|
||||
const fingerprints = {};
|
||||
|
||||
gulp.task("build-translation-fingerprints", () => {
|
||||
// Fingerprint full file of each language
|
||||
const files = fs.readdirSync(fullDir);
|
||||
gulp.task(
|
||||
"build-translation-fingerprints",
|
||||
function fingerprintTranslationFiles() {
|
||||
// Fingerprint full file of each language
|
||||
const files = fs.readdirSync(fullDir);
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
fingerprints[files[i].split(".")[0]] = {
|
||||
// In dev we create fake hashes
|
||||
hash: env.isProdBuild()
|
||||
? crypto
|
||||
.createHash("md5")
|
||||
.update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8"))
|
||||
.digest("hex")
|
||||
: "dev",
|
||||
};
|
||||
}
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
fingerprints[files[i].split(".")[0]] = {
|
||||
// In dev we create fake hashes
|
||||
hash: env.isProdBuild()
|
||||
? crypto
|
||||
.createHash("md5")
|
||||
.update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8"))
|
||||
.digest("hex")
|
||||
: "dev",
|
||||
};
|
||||
}
|
||||
|
||||
// In dev we create the file with the fake hash in the filename
|
||||
if (env.isProdBuild()) {
|
||||
mapFiles(outDir, ".json", (filename) => {
|
||||
const parsed = path.parse(filename);
|
||||
|
||||
@@ -334,114 +334,61 @@ gulp.task("build-translation-fingerprints", () => {
|
||||
}`
|
||||
);
|
||||
});
|
||||
|
||||
const stream = source("translationFingerprints.json");
|
||||
stream.write(JSON.stringify(fingerprints));
|
||||
process.nextTick(() => stream.end());
|
||||
return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir));
|
||||
}
|
||||
|
||||
const stream = source("translationFingerprints.json");
|
||||
stream.write(JSON.stringify(fingerprints));
|
||||
process.nextTick(() => stream.end());
|
||||
return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir));
|
||||
});
|
||||
|
||||
gulp.task("build-translation-fragment-supervisor", () =>
|
||||
gulp
|
||||
.src(fullDir + "/*.json")
|
||||
.pipe(transform((data) => data.supervisor))
|
||||
.pipe(
|
||||
rename((filePath) => {
|
||||
// In dev we create the file with the fake hash in the filename
|
||||
if (!env.isProdBuild()) {
|
||||
filePath.basename += "-dev";
|
||||
}
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(workDir + "/supervisor"))
|
||||
);
|
||||
|
||||
gulp.task("build-translation-flatten-supervisor", () =>
|
||||
gulp
|
||||
.src(workDir + "/supervisor/*.json")
|
||||
.pipe(
|
||||
transform((data) =>
|
||||
// Polymer.AppLocalizeBehavior requires flattened json
|
||||
flatten(data)
|
||||
)
|
||||
)
|
||||
.pipe(gulp.dest(outDir))
|
||||
);
|
||||
|
||||
gulp.task("build-translation-write-metadata", () =>
|
||||
gulp
|
||||
.src(
|
||||
[
|
||||
path.join(paths.translations_src, "translationMetadata.json"),
|
||||
workDir + "/testMetadata.json",
|
||||
workDir + "/translationFingerprints.json",
|
||||
],
|
||||
{ allowEmpty: true }
|
||||
)
|
||||
.pipe(merge({}))
|
||||
.pipe(
|
||||
transform((data) => {
|
||||
const newData = {};
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
// Filter out translations without native name.
|
||||
if (value.nativeName) {
|
||||
newData[key] = value;
|
||||
} else {
|
||||
console.warn(
|
||||
`Skipping language ${key}. Native name was not translated.`
|
||||
);
|
||||
}
|
||||
});
|
||||
return newData;
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
transform((data) => ({
|
||||
fragments: TRANSLATION_FRAGMENTS,
|
||||
translations: data,
|
||||
}))
|
||||
)
|
||||
.pipe(rename("translationMetadata.json"))
|
||||
.pipe(gulp.dest(workDir))
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"create-translations",
|
||||
gulp.series(
|
||||
env.isProdBuild() ? (done) => done() : "create-test-translation",
|
||||
"build-master-translation",
|
||||
"build-merged-translations",
|
||||
gulp.parallel(...splitTasks),
|
||||
"build-flattened-translations"
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"build-translations",
|
||||
gulp.series(
|
||||
gulp.parallel(
|
||||
"fetch-nightly-translations",
|
||||
gulp.series("clean-translations", "ensure-translations-build-dir")
|
||||
),
|
||||
"create-translations",
|
||||
"build-translation-fingerprints",
|
||||
"build-translation-write-metadata"
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"build-supervisor-translations",
|
||||
gulp.series(
|
||||
gulp.parallel(
|
||||
"fetch-nightly-translations",
|
||||
gulp.series("clean-translations", "ensure-translations-build-dir")
|
||||
),
|
||||
"clean-translations",
|
||||
"ensure-translations-build-dir",
|
||||
env.isProdBuild() ? (done) => done() : "create-test-translation",
|
||||
"build-master-translation",
|
||||
"build-merged-translations",
|
||||
"build-translation-fragment-supervisor",
|
||||
"build-translation-flatten-supervisor",
|
||||
gulp.parallel(...splitTasks),
|
||||
"build-flattened-translations",
|
||||
"build-translation-fingerprints",
|
||||
"build-translation-write-metadata"
|
||||
function writeMetadata() {
|
||||
return gulp
|
||||
.src(
|
||||
[
|
||||
path.join(paths.translations_src, "translationMetadata.json"),
|
||||
workDir + "/testMetadata.json",
|
||||
workDir + "/translationFingerprints.json",
|
||||
],
|
||||
{ allowEmpty: true }
|
||||
)
|
||||
.pipe(merge({}))
|
||||
.pipe(
|
||||
transform(function (data) {
|
||||
const newData = {};
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
// Filter out translations without native name.
|
||||
if (data[key].nativeName) {
|
||||
newData[key] = data[key];
|
||||
} else {
|
||||
console.warn(
|
||||
`Skipping language ${key}. Native name was not translated.`
|
||||
);
|
||||
}
|
||||
if (data[key]) newData[key] = value;
|
||||
});
|
||||
return newData;
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
transform((data) => ({
|
||||
fragments: TRANSLATION_FRAGMENTS,
|
||||
translations: data,
|
||||
}))
|
||||
)
|
||||
.pipe(rename("translationMetadata.json"))
|
||||
.pipe(gulp.dest(workDir));
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@@ -1,11 +1,9 @@
|
||||
// Tasks to run webpack.
|
||||
const fs = require("fs");
|
||||
const gulp = require("gulp");
|
||||
const webpack = require("webpack");
|
||||
const WebpackDevServer = require("webpack-dev-server");
|
||||
const log = require("fancy-log");
|
||||
const path = require("path");
|
||||
const env = require("../env");
|
||||
const paths = require("../paths");
|
||||
const {
|
||||
createAppConfig,
|
||||
@@ -20,13 +18,6 @@ const bothBuilds = (createConfigFunc, params) => [
|
||||
createConfigFunc({ ...params, latestBuild: false }),
|
||||
];
|
||||
|
||||
const isWsl =
|
||||
fs.existsSync("/proc/version") &&
|
||||
fs
|
||||
.readFileSync("/proc/version", "utf-8")
|
||||
.toLocaleLowerCase()
|
||||
.includes("microsoft");
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* compiler: import("webpack").Compiler,
|
||||
@@ -35,29 +26,26 @@ const isWsl =
|
||||
* listenHost?: string
|
||||
* }}
|
||||
*/
|
||||
const runDevServer = async ({
|
||||
const runDevServer = ({
|
||||
compiler,
|
||||
contentBase,
|
||||
port,
|
||||
listenHost = "localhost",
|
||||
}) => {
|
||||
const server = new WebpackDevServer(
|
||||
{
|
||||
open: true,
|
||||
host: listenHost,
|
||||
port,
|
||||
static: {
|
||||
directory: contentBase,
|
||||
watch: true,
|
||||
},
|
||||
},
|
||||
compiler
|
||||
);
|
||||
|
||||
await server.start();
|
||||
// Server listening
|
||||
log("[webpack-dev-server]", `Project is running at http://localhost:${port}`);
|
||||
};
|
||||
}) =>
|
||||
new WebpackDevServer(compiler, {
|
||||
open: true,
|
||||
watchContentBase: true,
|
||||
contentBase,
|
||||
}).listen(port, listenHost, function (err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
// Server listening
|
||||
log(
|
||||
"[webpack-dev-server]",
|
||||
`Project is running at http://localhost:${port}`
|
||||
);
|
||||
});
|
||||
|
||||
const doneHandler = (done) => (err, stats) => {
|
||||
if (err) {
|
||||
@@ -90,14 +78,13 @@ const prodBuild = (conf) =>
|
||||
|
||||
gulp.task("webpack-watch-app", () => {
|
||||
// This command will run forever because we don't close compiler
|
||||
webpack(
|
||||
process.env.ES5
|
||||
? bothBuilds(createAppConfig, { isProdBuild: false })
|
||||
: createAppConfig({ isProdBuild: false, latestBuild: true })
|
||||
).watch({ poll: isWsl }, doneHandler());
|
||||
webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch(
|
||||
{ ignored: /build-translations/ },
|
||||
doneHandler()
|
||||
);
|
||||
gulp.watch(
|
||||
path.join(paths.translations_src, "en.json"),
|
||||
gulp.series("create-translations", "copy-translations-app")
|
||||
gulp.series("build-translations", "copy-translations-app")
|
||||
);
|
||||
});
|
||||
|
||||
@@ -105,19 +92,17 @@ gulp.task("webpack-prod-app", () =>
|
||||
prodBuild(
|
||||
bothBuilds(createAppConfig, {
|
||||
isProdBuild: true,
|
||||
isStatsBuild: env.isStatsBuild(),
|
||||
isTestBuild: env.isTestBuild(),
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task("webpack-dev-server-demo", () =>
|
||||
gulp.task("webpack-dev-server-demo", () => {
|
||||
runDevServer({
|
||||
compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })),
|
||||
contentBase: paths.demo_output_root,
|
||||
port: 8090,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task("webpack-prod-demo", () =>
|
||||
prodBuild(
|
||||
@@ -127,15 +112,15 @@ gulp.task("webpack-prod-demo", () =>
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task("webpack-dev-server-cast", () =>
|
||||
gulp.task("webpack-dev-server-cast", () => {
|
||||
runDevServer({
|
||||
compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })),
|
||||
contentBase: paths.cast_output_root,
|
||||
port: 8080,
|
||||
// Accessible from the network, because that's how Cast hits it.
|
||||
listenHost: "0.0.0.0",
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task("webpack-prod-cast", () =>
|
||||
prodBuild(
|
||||
@@ -152,33 +137,25 @@ gulp.task("webpack-watch-hassio", () => {
|
||||
isProdBuild: false,
|
||||
latestBuild: true,
|
||||
})
|
||||
).watch({ ignored: /build/, poll: isWsl }, doneHandler());
|
||||
|
||||
gulp.watch(
|
||||
path.join(paths.translations_src, "en.json"),
|
||||
gulp.series("build-supervisor-translations", "copy-translations-supervisor")
|
||||
);
|
||||
).watch({}, doneHandler());
|
||||
});
|
||||
|
||||
gulp.task("webpack-prod-hassio", () =>
|
||||
prodBuild(
|
||||
bothBuilds(createHassioConfig, {
|
||||
isProdBuild: true,
|
||||
isStatsBuild: env.isStatsBuild(),
|
||||
isTestBuild: env.isTestBuild(),
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task("webpack-dev-server-gallery", () =>
|
||||
gulp.task("webpack-dev-server-gallery", () => {
|
||||
runDevServer({
|
||||
// We don't use the es5 build, but the dev server will fuck up the publicPath if we don't
|
||||
compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })),
|
||||
contentBase: paths.gallery_output_root,
|
||||
port: 8100,
|
||||
listenHost: "0.0.0.0",
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task("webpack-prod-gallery", () =>
|
||||
prodBuild(
|
||||
|
@@ -25,7 +25,6 @@ module.exports = {
|
||||
cast_output_es5: path.resolve(__dirname, "../cast/dist/frontend_es5"),
|
||||
|
||||
gallery_dir: path.resolve(__dirname, "../gallery"),
|
||||
gallery_build: path.resolve(__dirname, "../gallery/build"),
|
||||
gallery_output_root: path.resolve(__dirname, "../gallery/dist"),
|
||||
gallery_output_latest: path.resolve(
|
||||
__dirname,
|
||||
@@ -35,7 +34,6 @@ module.exports = {
|
||||
|
||||
hassio_dir: path.resolve(__dirname, "../hassio"),
|
||||
hassio_output_root: path.resolve(__dirname, "../hassio/build"),
|
||||
hassio_output_static: path.resolve(__dirname, "../hassio/build/static"),
|
||||
hassio_output_latest: path.resolve(
|
||||
__dirname,
|
||||
"../hassio/build/frontend_latest"
|
||||
|
@@ -81,13 +81,13 @@ module.exports = function (opts = {}) {
|
||||
opts.workerRegexp.flags
|
||||
);
|
||||
if (!workerRegexp.test(code)) {
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
const ms = new MagicString(code);
|
||||
// Reset the regexp
|
||||
workerRegexp.lastIndex = 0;
|
||||
for (;;) {
|
||||
while (true) {
|
||||
const match = workerRegexp.exec(code);
|
||||
if (!match) {
|
||||
break;
|
||||
@@ -98,7 +98,6 @@ module.exports = function (opts = {}) {
|
||||
// Parse the optional options object
|
||||
if (match[3] && match[3].length > 0) {
|
||||
// FIXME: ooooof!
|
||||
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
||||
optionsObject = new Function(`return ${match[3].slice(1)};`)();
|
||||
}
|
||||
delete optionsObject.type;
|
||||
@@ -111,14 +110,12 @@ module.exports = function (opts = {}) {
|
||||
}
|
||||
|
||||
// Find worker file and store it as a chunk with ID prefixed for our loader
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const resolvedWorkerFile = (await this.resolve(workerFile, id)).id;
|
||||
let chunkRefId;
|
||||
if (resolvedWorkerFile in refIds) {
|
||||
chunkRefId = refIds[resolvedWorkerFile];
|
||||
} else {
|
||||
this.addWatchFile(resolvedWorkerFile);
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const source = await getBundledWorker(
|
||||
resolvedWorkerFile,
|
||||
rollupOptions
|
||||
|
@@ -32,84 +32,88 @@ const createRollupConfig = ({
|
||||
publicPath,
|
||||
dontHash,
|
||||
isWDS,
|
||||
}) => ({
|
||||
/**
|
||||
* @type { import("rollup").InputOptions }
|
||||
*/
|
||||
inputOptions: {
|
||||
input: entry,
|
||||
// Some entry points contain no JavaScript. This setting silences a warning about that.
|
||||
// https://rollupjs.org/configuration-options/#preserveentrysignatures
|
||||
preserveEntrySignatures: false,
|
||||
plugins: [
|
||||
ignore({
|
||||
files: bundle
|
||||
.emptyPackages({ latestBuild })
|
||||
// TEMP HACK: Makes Rollup build work again
|
||||
.concat(
|
||||
require.resolve(
|
||||
"@webcomponents/scoped-custom-element-registry/scoped-custom-element-registry.min"
|
||||
)
|
||||
),
|
||||
}),
|
||||
resolve({
|
||||
extensions,
|
||||
preferBuiltins: false,
|
||||
browser: true,
|
||||
rootDir: paths.polymer_dir,
|
||||
}),
|
||||
commonjs(),
|
||||
json(),
|
||||
babel({
|
||||
...bundle.babelOptions({ latestBuild, isProdBuild }),
|
||||
extensions,
|
||||
babelHelpers: isWDS ? "inline" : "bundled",
|
||||
}),
|
||||
string({
|
||||
// Import certain extensions as strings
|
||||
include: [path.join(paths.polymer_dir, "node_modules/**/*.css")],
|
||||
}),
|
||||
replace(bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })),
|
||||
!isWDS &&
|
||||
manifest({
|
||||
publicPath,
|
||||
}) => {
|
||||
return {
|
||||
/**
|
||||
* @type { import("rollup").InputOptions }
|
||||
*/
|
||||
inputOptions: {
|
||||
input: entry,
|
||||
// Some entry points contain no JavaScript. This setting silences a warning about that.
|
||||
// https://rollupjs.org/guide/en/#preserveentrysignatures
|
||||
preserveEntrySignatures: false,
|
||||
plugins: [
|
||||
ignore({
|
||||
files: bundle.emptyPackages({ latestBuild }),
|
||||
}),
|
||||
!isWDS && worker(),
|
||||
!isWDS && dontHashPlugin({ dontHash }),
|
||||
!isWDS && isProdBuild && terser(bundle.terserOptions({ latestBuild })),
|
||||
!isWDS &&
|
||||
isStatsBuild &&
|
||||
visualizer({
|
||||
// https://github.com/btd/rollup-plugin-visualizer#options
|
||||
open: true,
|
||||
sourcemap: true,
|
||||
resolve({
|
||||
extensions,
|
||||
preferBuiltins: false,
|
||||
browser: true,
|
||||
rootDir: paths.polymer_dir,
|
||||
}),
|
||||
].filter(Boolean),
|
||||
},
|
||||
/**
|
||||
* @type { import("rollup").OutputOptions }
|
||||
*/
|
||||
outputOptions: {
|
||||
// https://rollupjs.org/configuration-options/#output-dir
|
||||
dir: outputPath,
|
||||
// https://rollupjs.org/configuration-options/#output-format
|
||||
format: latestBuild ? "es" : "systemjs",
|
||||
// https://rollupjs.org/configuration-options/#output-externallivebindings
|
||||
externalLiveBindings: false,
|
||||
// https://rollupjs.org/configuration-options/#output-entryfilenames
|
||||
// https://rollupjs.org/configuration-options/#output-chunkfilenames
|
||||
// https://rollupjs.org/configuration-options/#output-assetfilenames
|
||||
entryFileNames:
|
||||
isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js",
|
||||
chunkFileNames: isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js",
|
||||
assetFileNames: isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js",
|
||||
// https://rollupjs.org/configuration-options/#output-sourcemap
|
||||
sourcemap: isProdBuild ? true : "inline",
|
||||
},
|
||||
});
|
||||
commonjs({
|
||||
namedExports: {
|
||||
"js-yaml": ["safeDump", "safeLoad"],
|
||||
},
|
||||
}),
|
||||
json(),
|
||||
babel({
|
||||
...bundle.babelOptions({ latestBuild }),
|
||||
extensions,
|
||||
exclude: bundle.babelExclude(),
|
||||
babelHelpers: isWDS ? "inline" : "bundled",
|
||||
}),
|
||||
string({
|
||||
// Import certain extensions as strings
|
||||
include: [path.join(paths.polymer_dir, "node_modules/**/*.css")],
|
||||
}),
|
||||
replace(
|
||||
bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })
|
||||
),
|
||||
!isWDS &&
|
||||
manifest({
|
||||
publicPath,
|
||||
}),
|
||||
!isWDS && worker(),
|
||||
!isWDS && dontHashPlugin({ dontHash }),
|
||||
!isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)),
|
||||
!isWDS &&
|
||||
isStatsBuild &&
|
||||
visualizer({
|
||||
// https://github.com/btd/rollup-plugin-visualizer#options
|
||||
open: true,
|
||||
sourcemap: true,
|
||||
}),
|
||||
].filter(Boolean),
|
||||
},
|
||||
/**
|
||||
* @type { import("rollup").OutputOptions }
|
||||
*/
|
||||
outputOptions: {
|
||||
// https://rollupjs.org/guide/en/#outputdir
|
||||
dir: outputPath,
|
||||
// https://rollupjs.org/guide/en/#outputformat
|
||||
format: latestBuild ? "es" : "systemjs",
|
||||
// https://rollupjs.org/guide/en/#outputexternallivebindings
|
||||
externalLiveBindings: false,
|
||||
// https://rollupjs.org/guide/en/#outputentryfilenames
|
||||
// https://rollupjs.org/guide/en/#outputchunkfilenames
|
||||
// https://rollupjs.org/guide/en/#outputassetfilenames
|
||||
entryFileNames:
|
||||
isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js",
|
||||
chunkFileNames:
|
||||
isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js",
|
||||
assetFileNames:
|
||||
isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js",
|
||||
// https://rollupjs.org/guide/en/#outputsourcemap
|
||||
sourcemap: isProdBuild ? true : "inline",
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) =>
|
||||
createRollupConfig(
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => {
|
||||
return createRollupConfig(
|
||||
bundle.config.app({
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
@@ -117,24 +121,31 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) =>
|
||||
isWDS,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
|
||||
createRollupConfig(
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
return createRollupConfig(
|
||||
bundle.config.demo({
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
isStatsBuild,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
};
|
||||
|
||||
const createHassioConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
|
||||
const createHassioConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
|
||||
};
|
||||
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createRollupConfig(bundle.config.gallery({ isProdBuild, latestBuild }));
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createRollupConfig(
|
||||
bundle.config.gallery({ isProdBuild, latestBuild })
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createAppConfig,
|
||||
|
@@ -1,11 +1,10 @@
|
||||
const webpack = require("webpack");
|
||||
const path = require("path");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
||||
const log = require("fancy-log");
|
||||
const WebpackBar = require("webpackbar");
|
||||
const ManifestPlugin = require("webpack-manifest-plugin");
|
||||
const paths = require("./paths.js");
|
||||
const bundle = require("./bundle.js");
|
||||
const bundle = require("./bundle");
|
||||
const log = require("fancy-log");
|
||||
|
||||
class LogStartCompilePlugin {
|
||||
ignoredFirst = false;
|
||||
@@ -22,7 +21,6 @@ class LogStartCompilePlugin {
|
||||
}
|
||||
|
||||
const createWebpackConfig = ({
|
||||
name,
|
||||
entry,
|
||||
outputPath,
|
||||
publicPath,
|
||||
@@ -30,8 +28,6 @@ const createWebpackConfig = ({
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
isStatsBuild,
|
||||
isTestBuild,
|
||||
isHassioBuild,
|
||||
dontHash,
|
||||
}) => {
|
||||
if (!dontHash) {
|
||||
@@ -39,16 +35,10 @@ const createWebpackConfig = ({
|
||||
}
|
||||
const ignorePackages = bundle.ignorePackages({ latestBuild });
|
||||
return {
|
||||
name,
|
||||
mode: isProdBuild ? "production" : "development",
|
||||
target: ["web", latestBuild ? "es2017" : "es5"],
|
||||
// For tests/CI, source maps are skipped to gain build speed
|
||||
// For production, generate source maps for accurate stack traces without source code
|
||||
// For development, generate "cheap" versions that can map to original line numbers
|
||||
devtool: isTestBuild
|
||||
? false
|
||||
: isProdBuild
|
||||
? "nosources-source-map"
|
||||
devtool: isProdBuild
|
||||
? "cheap-module-source-map"
|
||||
: "eval-cheap-module-source-map",
|
||||
entry,
|
||||
node: false,
|
||||
@@ -56,21 +46,15 @@ const createWebpackConfig = ({
|
||||
rules: [
|
||||
{
|
||||
test: /\.m?js$|\.ts$/,
|
||||
exclude: bundle.babelExclude(),
|
||||
use: {
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
...bundle.babelOptions({ latestBuild, isProdBuild, isTestBuild }),
|
||||
cacheDirectory: !isProdBuild,
|
||||
cacheCompression: false,
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
fullySpecified: false,
|
||||
options: bundle.babelOptions({ latestBuild }),
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
type: "asset/source",
|
||||
use: "raw-loader",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -79,15 +63,12 @@ const createWebpackConfig = ({
|
||||
new TerserPlugin({
|
||||
parallel: true,
|
||||
extractComments: true,
|
||||
terserOptions: bundle.terserOptions({ latestBuild, isTestBuild }),
|
||||
terserOptions: bundle.terserOptions(latestBuild),
|
||||
}),
|
||||
],
|
||||
moduleIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
|
||||
chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
|
||||
},
|
||||
plugins: [
|
||||
!isStatsBuild && new WebpackBar({ fancy: !isProdBuild }),
|
||||
new WebpackManifestPlugin({
|
||||
new ManifestPlugin({
|
||||
// Only include the JS of entrypoints
|
||||
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
|
||||
}),
|
||||
@@ -127,101 +108,69 @@ const createWebpackConfig = ({
|
||||
},
|
||||
}),
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
new RegExp(
|
||||
bundle.emptyPackages({ latestBuild, isHassioBuild }).join("|")
|
||||
),
|
||||
new RegExp(bundle.emptyPackages({ latestBuild }).join("|")),
|
||||
path.resolve(paths.polymer_dir, "src/util/empty.js")
|
||||
),
|
||||
// We need to change the import of the polyfill for EventTarget, so we replace the polyfill file with our customized one
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
new RegExp(
|
||||
require.resolve(
|
||||
"lit-virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
|
||||
)
|
||||
),
|
||||
path.resolve(paths.polymer_dir, "src/resources/EventTarget-ponyfill.js")
|
||||
),
|
||||
!isProdBuild && new LogStartCompilePlugin(),
|
||||
].filter(Boolean),
|
||||
resolve: {
|
||||
extensions: [".ts", ".js", ".json"],
|
||||
alias: {
|
||||
"lit/decorators$": "lit/decorators.js",
|
||||
"lit/directive$": "lit/directive.js",
|
||||
"lit/directives/until$": "lit/directives/until.js",
|
||||
"lit/directives/class-map$": "lit/directives/class-map.js",
|
||||
"lit/directives/style-map$": "lit/directives/style-map.js",
|
||||
"lit/directives/if-defined$": "lit/directives/if-defined.js",
|
||||
"lit/directives/guard$": "lit/directives/guard.js",
|
||||
"lit/directives/cache$": "lit/directives/cache.js",
|
||||
"lit/directives/repeat$": "lit/directives/repeat.js",
|
||||
"lit/polyfill-support$": "lit/polyfill-support.js",
|
||||
"@lit-labs/virtualizer/layouts/grid":
|
||||
"@lit-labs/virtualizer/layouts/grid.js",
|
||||
},
|
||||
},
|
||||
output: {
|
||||
filename: ({ chunk }) => {
|
||||
if (!isProdBuild || isStatsBuild || dontHash.has(chunk.name)) {
|
||||
if (!isProdBuild || dontHash.has(chunk.name)) {
|
||||
return `${chunk.name}.js`;
|
||||
}
|
||||
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
|
||||
},
|
||||
chunkFilename:
|
||||
isProdBuild && !isStatsBuild ? "[chunkhash:8].js" : "[id].chunk.js",
|
||||
isProdBuild && !isStatsBuild
|
||||
? "chunk.[chunkhash].js"
|
||||
: "[name].chunk.js",
|
||||
path: outputPath,
|
||||
publicPath,
|
||||
// To silence warning in worker plugin
|
||||
globalObject: "self",
|
||||
// Since production source maps don't include sources, we need to point to them elsewhere
|
||||
// For dependencies, just provide the path (no source in browser)
|
||||
// Otherwise, point to the raw code on GitHub for browser to load
|
||||
devtoolModuleFilenameTemplate:
|
||||
!isTestBuild && isProdBuild
|
||||
? (info) => {
|
||||
const sourcePath = info.resourcePath.replace(/^\.\//, "");
|
||||
if (
|
||||
sourcePath.startsWith("node_modules") ||
|
||||
sourcePath.startsWith("webpack")
|
||||
) {
|
||||
return `no-source/${sourcePath}`;
|
||||
}
|
||||
return `${bundle.sourceMapURL()}/${sourcePath}`;
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
experiments: {
|
||||
topLevelAwait: true,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const createAppConfig = ({
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
isStatsBuild,
|
||||
isTestBuild,
|
||||
}) =>
|
||||
createWebpackConfig(
|
||||
bundle.config.app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild })
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
return createWebpackConfig(
|
||||
bundle.config.app({ isProdBuild, latestBuild, isStatsBuild })
|
||||
);
|
||||
};
|
||||
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
|
||||
createWebpackConfig(
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
return createWebpackConfig(
|
||||
bundle.config.demo({ isProdBuild, latestBuild, isStatsBuild })
|
||||
);
|
||||
};
|
||||
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
};
|
||||
|
||||
const createHassioConfig = ({
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
isStatsBuild,
|
||||
isTestBuild,
|
||||
}) =>
|
||||
createWebpackConfig(
|
||||
bundle.config.hassio({
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
isStatsBuild,
|
||||
isTestBuild,
|
||||
})
|
||||
const createHassioConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createWebpackConfig(
|
||||
bundle.config.hassio({ isProdBuild, latestBuild })
|
||||
);
|
||||
};
|
||||
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createWebpackConfig(bundle.config.gallery({ isProdBuild, latestBuild }));
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createWebpackConfig(
|
||||
bundle.config.gallery({ isProdBuild, latestBuild })
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createAppConfig,
|
||||
|
@@ -1,9 +0,0 @@
|
||||
# These redirects are handled by Netlify
|
||||
#
|
||||
|
||||
# Some custom cards are not prefixing the instance URL when fetching data
|
||||
# and can end up fetching the data from the Cast domain instead of HA.
|
||||
# This will make sure that some common ones are replaced with a placeholder.
|
||||
/api/camera_proxy/* /images/google-nest-hub.png
|
||||
/api/camera_proxy_stream/* /images/google-nest-hub.png
|
||||
/api/media_player_proxy/* /images/google-nest-hub.png
|
@@ -139,7 +139,7 @@
|
||||
Your authentication credentials or Home Assistant url are never sent
|
||||
to the Cloud. You can validate this behavior in
|
||||
<a
|
||||
href="https://github.com/home-assistant/frontend/tree/dev/cast"
|
||||
href="https://github.com/home-assistant/home-assistant-polymer/tree/dev/cast"
|
||||
target="_blank"
|
||||
>the source code</a
|
||||
>.
|
||||
@@ -213,7 +213,7 @@
|
||||
</p>
|
||||
<ul>
|
||||
<li>Google Chrome (all platforms except iOS)</li>
|
||||
<li>Microsoft Edge (all platforms except iOS)</li>
|
||||
<li>Microsoft Edge (all platforms)</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
@@ -1,46 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
|
||||
<style>
|
||||
body {
|
||||
--logo-image: url('https://www.home-assistant.io/images/home-assistant-logo.svg');
|
||||
--logo-repeat: no-repeat;
|
||||
--playback-logo-image: url('https://www.home-assistant.io/images/home-assistant-logo.svg');
|
||||
--theme-hue: 200;
|
||||
--progress-color: #03a9f4;
|
||||
--splash-image: url('https://home-assistant.io/images/cast/splash.png');
|
||||
--splash-size: cover;
|
||||
--background-color: #41bdf5;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var _gaq=[['_setAccount','UA-57927901-10'],['_trackPageview']];
|
||||
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
|
||||
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
|
||||
s.parentNode.insertBefore(g,s)}(document,'script'));
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<%= renderTemplate('_js_base') %>
|
||||
|
||||
<cast-media-player></cast-media-player>
|
||||
|
||||
<script>
|
||||
import("<%= latestMediaJS %>");
|
||||
window.latestJS = true;
|
||||
</script>
|
||||
|
||||
<script>
|
||||
if (!window.latestJS) {
|
||||
<% if (useRollup) { %>
|
||||
_ls("/static/js/s.min.js").onload = function() {
|
||||
System.import("<%= es5MediaJS %>");
|
||||
};
|
||||
<% } else { %>
|
||||
_ls("<%= es5MediaJS %>");
|
||||
<% } %>
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@@ -1,10 +1,16 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { mdiCast, mdiCastConnected } from "@mdi/js";
|
||||
import "@polymer/paper-item/paper-icon-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import { Auth, Connection } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { CastManager } from "../../../../src/cast/cast_manager";
|
||||
import {
|
||||
castSendShowLovelaceView,
|
||||
@@ -18,7 +24,6 @@ import {
|
||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||
import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute";
|
||||
import "../../../../src/components/ha-icon";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import {
|
||||
getLegacyLovelaceCollection,
|
||||
getLovelaceCollection,
|
||||
@@ -27,6 +32,7 @@ import {
|
||||
import "../../../../src/layouts/hass-loading-screen";
|
||||
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
|
||||
import "./hc-layout";
|
||||
import "@material/mwc-button/mwc-button";
|
||||
|
||||
@customElement("hc-cast")
|
||||
class HcCast extends LitElement {
|
||||
@@ -36,19 +42,21 @@ class HcCast extends LitElement {
|
||||
|
||||
@property() public castManager!: CastManager;
|
||||
|
||||
@state() private askWrite = false;
|
||||
@internalProperty() private askWrite = false;
|
||||
|
||||
@state() private lovelaceConfig?: LovelaceConfig | null;
|
||||
@internalProperty() private lovelaceConfig?: LovelaceConfig | null;
|
||||
|
||||
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 =
|
||||
this.castManager.castState === "NO_DEVICES_AVAILABLE"
|
||||
? html`
|
||||
<p>There were no suitable Chromecast devices to cast to found.</p>
|
||||
<p>
|
||||
There were no suitable Chromecast devices to cast to found.
|
||||
</p>
|
||||
`
|
||||
: undefined;
|
||||
|
||||
@@ -75,7 +83,7 @@ class HcCast extends LitElement {
|
||||
? html`
|
||||
<p class="center-item">
|
||||
<mwc-button raised @click=${this._handleLaunch}>
|
||||
<ha-svg-icon .path=${mdiCast}></ha-svg-icon>
|
||||
<ha-icon icon="hass:cast"></ha-icon>
|
||||
Start Casting
|
||||
</mwc-button>
|
||||
</p>
|
||||
@@ -88,7 +96,7 @@ class HcCast extends LitElement {
|
||||
>
|
||||
${(this.lovelaceConfig
|
||||
? this.lovelaceConfig.views
|
||||
: [generateDefaultViewConfig({}, {}, {}, {}, () => "")]
|
||||
: [generateDefaultViewConfig([], [], [], {}, () => "")]
|
||||
).map(
|
||||
(view, idx) => html`
|
||||
<paper-icon-item
|
||||
@@ -113,7 +121,7 @@ class HcCast extends LitElement {
|
||||
${this.castManager.status
|
||||
? html`
|
||||
<mwc-button @click=${this._handleLaunch}>
|
||||
<ha-svg-icon .path=${mdiCastConnected}></ha-svg-icon>
|
||||
<ha-icon icon="hass:cast-connected"></ha-icon>
|
||||
Manage
|
||||
</mwc-button>
|
||||
`
|
||||
@@ -181,7 +189,7 @@ class HcCast extends LitElement {
|
||||
private async _handlePickView(ev: Event) {
|
||||
const path = (ev.currentTarget as any).getAttribute("data-path");
|
||||
await ensureConnectedCastSession(this.castManager!, this.auth!);
|
||||
castSendShowLovelaceView(this.castManager, this.auth.data.hassUrl, path);
|
||||
castSendShowLovelaceView(this.castManager, path);
|
||||
}
|
||||
|
||||
private async _handleLogout() {
|
||||
@@ -193,12 +201,12 @@ class HcCast extends LitElement {
|
||||
}
|
||||
this.connection.close();
|
||||
location.reload();
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
alert("Unable to log out!");
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
.center-item {
|
||||
display: flex;
|
||||
@@ -235,7 +243,7 @@ class HcCast extends LitElement {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
mwc-button ha-svg-icon {
|
||||
mwc-button ha-icon {
|
||||
margin-right: 8px;
|
||||
height: 18px;
|
||||
}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import "@material/mwc-button";
|
||||
import { mdiCastConnected, mdiCast } from "@mdi/js";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import {
|
||||
Auth,
|
||||
@@ -12,15 +11,22 @@ import {
|
||||
getAuth,
|
||||
getAuthOptions,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
internalProperty,
|
||||
} from "lit-element";
|
||||
import { CastManager, getCastManager } from "../../../../src/cast/cast_manager";
|
||||
import { castSendShowDemo } from "../../../../src/cast/receiver_messages";
|
||||
import {
|
||||
loadTokens,
|
||||
saveTokens,
|
||||
} from "../../../../src/common/auth/token_storage";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import "../../../../src/components/ha-icon";
|
||||
import "../../../../src/layouts/hass-loading-screen";
|
||||
import { registerServiceWorker } from "../../../../src/util/register-service-worker";
|
||||
import "./hc-layout";
|
||||
@@ -54,19 +60,19 @@ const INTRO = html`
|
||||
|
||||
@customElement("hc-connect")
|
||||
export class HcConnect extends LitElement {
|
||||
@state() private loading = false;
|
||||
@internalProperty() private loading = false;
|
||||
|
||||
// If we had stored credentials but we cannot connect,
|
||||
// show a screen asking retry or logout.
|
||||
@state() private cannotConnect = false;
|
||||
@internalProperty() private cannotConnect = false;
|
||||
|
||||
@state() private error?: string | TemplateResult;
|
||||
@internalProperty() private error?: string | TemplateResult;
|
||||
|
||||
@state() private auth?: Auth;
|
||||
@internalProperty() private auth?: Auth;
|
||||
|
||||
@state() private connection?: Connection;
|
||||
@internalProperty() private connection?: Connection;
|
||||
|
||||
@state() private castManager?: CastManager | null;
|
||||
@internalProperty() private castManager?: CastManager | null;
|
||||
|
||||
private openDemo = false;
|
||||
|
||||
@@ -80,7 +86,9 @@ export class HcConnect extends LitElement {
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<a href="/">
|
||||
<mwc-button> Retry </mwc-button>
|
||||
<mwc-button>
|
||||
Retry
|
||||
</mwc-button>
|
||||
</a>
|
||||
<div class="spacer"></div>
|
||||
<mwc-button @click=${this._handleLogout}>Log out</mwc-button>
|
||||
@@ -128,11 +136,11 @@ export class HcConnect extends LitElement {
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._handleDemo}>
|
||||
Show Demo
|
||||
<ha-svg-icon
|
||||
.path=${this.castManager.castState === "CONNECTED"
|
||||
? mdiCastConnected
|
||||
: mdiCast}
|
||||
></ha-svg-icon>
|
||||
<ha-icon
|
||||
.icon=${this.castManager.castState === "CONNECTED"
|
||||
? "hass:cast-connected"
|
||||
: "hass:cast"}
|
||||
></ha-icon>
|
||||
</mwc-button>
|
||||
<div class="spacer"></div>
|
||||
<mwc-button @click=${this._handleConnect}>Authorize</mwc-button>
|
||||
@@ -213,7 +221,7 @@ export class HcConnect extends LitElement {
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(value);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this.error = "Invalid URL";
|
||||
return;
|
||||
}
|
||||
@@ -241,7 +249,7 @@ export class HcConnect extends LitElement {
|
||||
try {
|
||||
this.loading = true;
|
||||
auth = await getAuth(options);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (init === "saved-tokens" && err === ERR_CANNOT_CONNECT) {
|
||||
this.cannotConnect = true;
|
||||
return;
|
||||
@@ -260,7 +268,7 @@ export class HcConnect extends LitElement {
|
||||
|
||||
try {
|
||||
conn = await createConnection({ auth });
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
// In case of saved tokens, silently solve problems.
|
||||
if (init === "saved-tokens") {
|
||||
if (err === ERR_CANNOT_CONNECT) {
|
||||
@@ -286,12 +294,12 @@ export class HcConnect extends LitElement {
|
||||
try {
|
||||
saveTokens(null);
|
||||
location.reload();
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
alert("Unable to log out!");
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
.card-content a {
|
||||
color: var(--primary-color);
|
||||
@@ -308,7 +316,7 @@ export class HcConnect extends LitElement {
|
||||
color: darkred;
|
||||
}
|
||||
|
||||
mwc-button ha-svg-icon {
|
||||
mwc-button ha-icon {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
|
@@ -4,8 +4,15 @@ import {
|
||||
getUser,
|
||||
HassUser,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../../src/components/ha-card";
|
||||
|
||||
@customElement("hc-layout")
|
||||
@@ -22,11 +29,7 @@ class HcLayout extends LitElement {
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="layout">
|
||||
<img
|
||||
class="hero"
|
||||
alt="A Google Nest Hub with a Home Assistant dashboard on its screen"
|
||||
src="/images/google-nest-hub.png"
|
||||
/>
|
||||
<img class="hero" src="/images/google-nest-hub.png" />
|
||||
<h1 class="card-header">
|
||||
Home Assistant Cast${this.subtitle ? ` – ${this.subtitle}` : ""}
|
||||
${this.auth
|
||||
@@ -48,7 +51,7 @@ class HcLayout extends LitElement {
|
||||
<div class="footer">
|
||||
<a href="./faq.html">Frequently Asked Questions</a> – Found a bug?
|
||||
<a
|
||||
href="https://github.com/home-assistant/frontend/issues"
|
||||
href="https://github.com/home-assistant/home-assistant-polymer/issues"
|
||||
target="_blank"
|
||||
>Let us know!</a
|
||||
>
|
||||
@@ -66,7 +69,7 @@ class HcLayout extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
display: flex;
|
||||
@@ -95,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,22 +0,0 @@
|
||||
const castContext = cast.framework.CastReceiverContext.getInstance();
|
||||
|
||||
const playerManager = castContext.getPlayerManager();
|
||||
|
||||
playerManager.setMessageInterceptor(
|
||||
cast.framework.messages.MessageType.LOAD,
|
||||
(loadRequestData) => {
|
||||
const media = loadRequestData.media;
|
||||
// Special handling if it came from Google Assistant
|
||||
if (media.entity) {
|
||||
media.contentId = media.entity;
|
||||
media.streamType = cast.framework.messages.StreamType.LIVE;
|
||||
media.contentType = "application/vnd.apple.mpegurl";
|
||||
// @ts-ignore
|
||||
media.hlsVideoSegmentFormat =
|
||||
cast.framework.messages.HlsVideoSegmentFormat.FMP4;
|
||||
}
|
||||
return loadRequestData;
|
||||
}
|
||||
);
|
||||
|
||||
castContext.start();
|
@@ -5,8 +5,8 @@ import {
|
||||
import { castContext } from "../cast_context";
|
||||
|
||||
export const castDemoLovelace: () => LovelaceConfig = () => {
|
||||
const touchSupported =
|
||||
castContext.getDeviceCapabilities().touch_input_supported;
|
||||
const touchSupported = castContext.getDeviceCapabilities()
|
||||
.touch_input_supported;
|
||||
return {
|
||||
views: [
|
||||
{
|
||||
|
@@ -8,9 +8,6 @@ import { ReceivedMessage } from "./types";
|
||||
|
||||
const lovelaceController = new HcMain();
|
||||
document.body.append(lovelaceController);
|
||||
lovelaceController.addEventListener("cast-view-changed", (ev) => {
|
||||
playDummyMedia(ev.detail.title);
|
||||
});
|
||||
|
||||
const mediaPlayer = document.createElement("cast-media-player");
|
||||
mediaPlayer.style.display = "none";
|
||||
@@ -31,31 +28,6 @@ const setTouchControlsVisibility = (visible: boolean) => {
|
||||
}
|
||||
};
|
||||
|
||||
let timeOut: number | undefined;
|
||||
|
||||
const playDummyMedia = (viewTitle?: string) => {
|
||||
const loadRequestData = new cast.framework.messages.LoadRequestData();
|
||||
loadRequestData.autoplay = true;
|
||||
loadRequestData.media = new cast.framework.messages.MediaInformation();
|
||||
loadRequestData.media.contentId =
|
||||
"https://cast.home-assistant.io/images/google-nest-hub.png";
|
||||
loadRequestData.media.contentType = "image/jpeg";
|
||||
loadRequestData.media.streamType = cast.framework.messages.StreamType.NONE;
|
||||
const metadata = new cast.framework.messages.GenericMediaMetadata();
|
||||
metadata.title = viewTitle;
|
||||
loadRequestData.media.metadata = metadata;
|
||||
|
||||
loadRequestData.requestId = 0;
|
||||
playerManager.load(loadRequestData);
|
||||
if (timeOut) {
|
||||
clearTimeout(timeOut);
|
||||
timeOut = undefined;
|
||||
}
|
||||
if (castContext.getDeviceCapabilities().touch_input_supported) {
|
||||
timeOut = window.setTimeout(() => playDummyMedia(viewTitle), 540000); // repeat every 9 minutes to keep it active (gets deactivated after 10 minutes)
|
||||
}
|
||||
};
|
||||
|
||||
const showLovelaceController = () => {
|
||||
mediaPlayer.style.display = "none";
|
||||
lovelaceController.style.display = "initial";
|
||||
@@ -79,7 +51,6 @@ const showMediaPlayer = () => {
|
||||
--progress-color: #03a9f4;
|
||||
--splash-image: url('https://home-assistant.io/images/cast/splash.png');
|
||||
--splash-size: cover;
|
||||
--background-color: #41bdf5;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
@@ -92,6 +63,22 @@ options.customNamespaces = {
|
||||
[CAST_NS]: cast.framework.system.MessageType.JSON,
|
||||
};
|
||||
|
||||
// The docs say we need to set options.touchScreenOptimizeApp = true
|
||||
// https://developers.google.com/cast/docs/caf_receiver/customize_ui#accessing_ui_controls
|
||||
// This doesn't work.
|
||||
// @ts-ignore
|
||||
options.touchScreenOptimizedApp = true;
|
||||
|
||||
// The class reference say we can set a uiConfig in options to set it
|
||||
// https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.CastReceiverOptions#uiConfig
|
||||
// This doesn't work either.
|
||||
// @ts-ignore
|
||||
options.uiConfig = new cast.framework.ui.UiConfig();
|
||||
// @ts-ignore
|
||||
options.uiConfig.touchScreenOptimizedApp = true;
|
||||
|
||||
castContext.setInactivityTimeout(86400); // 1 day
|
||||
|
||||
castContext.addCustomMessageListener(
|
||||
CAST_NS,
|
||||
// @ts-ignore
|
||||
@@ -116,12 +103,6 @@ const playerManager = castContext.getPlayerManager();
|
||||
playerManager.setMessageInterceptor(
|
||||
cast.framework.messages.MessageType.LOAD,
|
||||
(loadRequestData) => {
|
||||
if (
|
||||
loadRequestData.media.contentId ===
|
||||
"https://cast.home-assistant.io/images/google-nest-hub.png"
|
||||
) {
|
||||
return loadRequestData;
|
||||
}
|
||||
// We received a play media command, hide Lovelace and show media player
|
||||
showMediaPlayer();
|
||||
const media = loadRequestData.media;
|
||||
|
@@ -1,5 +1,10 @@
|
||||
import { html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { mockHistory } from "../../../../demo/src/stubs/history";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import {
|
||||
@@ -16,11 +21,11 @@ import "./hc-lovelace";
|
||||
class HcDemo extends HassElement {
|
||||
@property({ attribute: false }) public lovelacePath!: string;
|
||||
|
||||
@state() private _lovelaceConfig?: LovelaceConfig;
|
||||
@internalProperty() private _lovelaceConfig?: LovelaceConfig;
|
||||
|
||||
protected render() {
|
||||
protected render(): TemplateResult {
|
||||
if (!this._lovelaceConfig) {
|
||||
return nothing;
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<hc-lovelace
|
||||
@@ -33,10 +38,10 @@ class HcDemo extends HassElement {
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this._initializeHass();
|
||||
this._initialize();
|
||||
}
|
||||
|
||||
private async _initializeHass() {
|
||||
private async _initialize() {
|
||||
const initial: Partial<MockHomeAssistant> = {
|
||||
// Override updateHass so that the correct hass lifecycle methods are called
|
||||
updateHass: (hassUpdate: Partial<HomeAssistant>) =>
|
||||
|
@@ -1,5 +1,12 @@
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
@customElement("hc-launch-screen")
|
||||
@@ -12,7 +19,6 @@ class HcLaunchScreen extends LitElement {
|
||||
return html`
|
||||
<div class="container">
|
||||
<img
|
||||
alt="Home Assistant logo on left, Nabu Casa logo on right, and red heart in center"
|
||||
src="https://www.home-assistant.io/images/blog/2018-09-thinking-big/social.png"
|
||||
/>
|
||||
<div class="status">
|
||||
@@ -23,7 +29,7 @@ class HcLaunchScreen extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
display: block;
|
||||
|
@@ -1,15 +1,18 @@
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import { Lovelace } from "../../../../src/panels/lovelace/types";
|
||||
import "../../../../src/panels/lovelace/views/hui-view";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import "./hc-launch-screen";
|
||||
|
||||
(window as any).loadCardHelpers = () =>
|
||||
import("../../../../src/panels/lovelace/custom-card-helpers");
|
||||
|
||||
@customElement("hc-lovelace")
|
||||
class HcLovelace extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@@ -18,9 +21,7 @@ class HcLovelace extends LitElement {
|
||||
|
||||
@property() public viewPath?: string | number;
|
||||
|
||||
@property() public urlPath: string | null = null;
|
||||
|
||||
@query("hui-view") private _huiView?: HTMLElement;
|
||||
public urlPath?: string | null;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const index = this._viewIndex;
|
||||
@@ -34,12 +35,11 @@ class HcLovelace extends LitElement {
|
||||
}
|
||||
const lovelace: Lovelace = {
|
||||
config: this.lovelaceConfig,
|
||||
rawConfig: this.lovelaceConfig,
|
||||
editMode: false,
|
||||
urlPath: this.urlPath,
|
||||
urlPath: this.urlPath!,
|
||||
enableFullEditMode: () => undefined,
|
||||
mode: "storage",
|
||||
locale: this.hass.locale,
|
||||
language: "en",
|
||||
saveConfig: async () => undefined,
|
||||
deleteConfig: async () => undefined,
|
||||
setEditMode: () => undefined,
|
||||
@@ -60,32 +60,17 @@ class HcLovelace extends LitElement {
|
||||
const index = this._viewIndex;
|
||||
|
||||
if (index !== undefined) {
|
||||
const dashboardTitle = this.lovelaceConfig.title || this.urlPath;
|
||||
|
||||
const viewTitle =
|
||||
this.lovelaceConfig.views[index].title ||
|
||||
this.lovelaceConfig.views[index].path;
|
||||
|
||||
fireEvent(this, "cast-view-changed", {
|
||||
title:
|
||||
dashboardTitle || viewTitle
|
||||
? `${dashboardTitle || ""}${
|
||||
dashboardTitle && viewTitle ? ": " : ""
|
||||
}${viewTitle || ""}`
|
||||
: undefined,
|
||||
});
|
||||
|
||||
const configBackground =
|
||||
this.lovelaceConfig.views[index].background ||
|
||||
this.lovelaceConfig.background;
|
||||
|
||||
if (configBackground) {
|
||||
this._huiView!.style.setProperty(
|
||||
(this.shadowRoot!.querySelector(
|
||||
"hui-view"
|
||||
) as HTMLElement)!.style.setProperty(
|
||||
"--lovelace-background",
|
||||
configBackground
|
||||
);
|
||||
} else {
|
||||
this._huiView!.style.removeProperty("--lovelace-background");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,11 +90,10 @@ class HcLovelace extends LitElement {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
min-height: 100vh;
|
||||
height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
@@ -118,22 +102,12 @@ class HcLovelace extends LitElement {
|
||||
:host > * {
|
||||
flex: 1;
|
||||
}
|
||||
hui-view {
|
||||
background: var(--lovelace-background, var(--primary-background-color));
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
export interface CastViewChanged {
|
||||
title: string | undefined;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hc-lovelace": HcLovelace;
|
||||
}
|
||||
interface HASSDomEvents {
|
||||
"cast-view-changed": CastViewChanged;
|
||||
}
|
||||
}
|
||||
|
@@ -3,8 +3,12 @@ import {
|
||||
getAuth,
|
||||
UnsubscribeFunc,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { CAST_NS } from "../../../../src/cast/const";
|
||||
import {
|
||||
ConnectMessage,
|
||||
@@ -13,11 +17,7 @@ import {
|
||||
ShowDemoMessage,
|
||||
ShowLovelaceViewMessage,
|
||||
} from "../../../../src/cast/receiver_messages";
|
||||
import {
|
||||
ReceiverErrorCode,
|
||||
ReceiverErrorMessage,
|
||||
ReceiverStatusMessage,
|
||||
} from "../../../../src/cast/sender_messages";
|
||||
import { ReceiverStatusMessage } from "../../../../src/cast/sender_messages";
|
||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
|
||||
import {
|
||||
@@ -33,22 +33,21 @@ import { castContext } from "../cast_context";
|
||||
import "./hc-launch-screen";
|
||||
|
||||
let resourcesLoaded = false;
|
||||
|
||||
@customElement("hc-main")
|
||||
export class HcMain extends HassElement {
|
||||
@state() private _showDemo = false;
|
||||
@internalProperty() private _showDemo = false;
|
||||
|
||||
@state() private _lovelaceConfig?: LovelaceConfig;
|
||||
@internalProperty() private _lovelaceConfig?: LovelaceConfig;
|
||||
|
||||
@state() private _lovelacePath: string | number | null = null;
|
||||
@internalProperty() private _lovelacePath: string | number | null = null;
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
@state() private _urlPath?: string | null;
|
||||
|
||||
private _hassUUID?: string;
|
||||
@internalProperty() private _error?: string;
|
||||
|
||||
private _unsubLovelace?: UnsubscribeFunc;
|
||||
|
||||
private _urlPath?: string | null;
|
||||
|
||||
public processIncomingMessage(msg: HassMessage) {
|
||||
if (msg.type === "connect") {
|
||||
this._handleConnectMessage(msg);
|
||||
@@ -73,10 +72,8 @@ export class HcMain extends HassElement {
|
||||
!this._lovelaceConfig ||
|
||||
this._lovelacePath === null ||
|
||||
// Guard against part of HA not being loaded yet.
|
||||
!this.hass ||
|
||||
!this.hass.states ||
|
||||
!this.hass.config ||
|
||||
!this.hass.services
|
||||
(this.hass &&
|
||||
(!this.hass.states || !this.hass.config || !this.hass.services))
|
||||
) {
|
||||
return html`
|
||||
<hc-launch-screen
|
||||
@@ -114,7 +111,6 @@ export class HcMain extends HassElement {
|
||||
this._sendStatus();
|
||||
}
|
||||
});
|
||||
this.addEventListener("dialog-closed", this._dialogClosed);
|
||||
}
|
||||
|
||||
private _sendStatus(senderId?: string) {
|
||||
@@ -126,8 +122,7 @@ export class HcMain extends HassElement {
|
||||
|
||||
if (this.hass) {
|
||||
status.hassUrl = this.hass.auth.data.hassUrl;
|
||||
status.hassUUID = this._hassUUID;
|
||||
status.lovelacePath = this._lovelacePath;
|
||||
status.lovelacePath = this._lovelacePath!;
|
||||
status.urlPath = this._urlPath;
|
||||
}
|
||||
|
||||
@@ -140,43 +135,7 @@ export class HcMain extends HassElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _sendError(
|
||||
error_code: number,
|
||||
error_message: string,
|
||||
senderId?: string
|
||||
) {
|
||||
const error: ReceiverErrorMessage = {
|
||||
type: "receiver_error",
|
||||
error_code,
|
||||
error_message,
|
||||
};
|
||||
|
||||
if (senderId) {
|
||||
this.sendMessage(senderId, error);
|
||||
} else {
|
||||
for (const sender of castContext.getSenders()) {
|
||||
this.sendMessage(sender.id, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _dialogClosed = () => {
|
||||
document.body.setAttribute("style", "overflow-y: auto !important");
|
||||
};
|
||||
|
||||
private async _handleGetStatusMessage(msg: GetStatusMessage) {
|
||||
if (
|
||||
(this.hass && msg.hassUUID && msg.hassUUID !== this._hassUUID) ||
|
||||
(this.hass && msg.hassUrl && msg.hassUrl !== this.hass.auth.data.hassUrl)
|
||||
) {
|
||||
this._error = "Not connected to the same Home Assistant instance.";
|
||||
this._sendError(
|
||||
ReceiverErrorCode.WRONG_INSTANCE,
|
||||
this._error,
|
||||
msg.senderId!
|
||||
);
|
||||
}
|
||||
|
||||
this._sendStatus(msg.senderId!);
|
||||
}
|
||||
|
||||
@@ -193,20 +152,15 @@ export class HcMain extends HassElement {
|
||||
expires_in: 0,
|
||||
}),
|
||||
});
|
||||
this._hassUUID = msg.hassUUID;
|
||||
} catch (err: any) {
|
||||
const errorMessage = this._getErrorMessage(err);
|
||||
this._error = errorMessage;
|
||||
this._sendError(err, errorMessage);
|
||||
} catch (err) {
|
||||
this._error = this._getErrorMessage(err);
|
||||
return;
|
||||
}
|
||||
let connection;
|
||||
try {
|
||||
connection = await createConnection({ auth });
|
||||
} catch (err: any) {
|
||||
const errorMessage = this._getErrorMessage(err);
|
||||
this._error = errorMessage;
|
||||
this._sendError(err, errorMessage);
|
||||
} catch (err) {
|
||||
this._error = this._getErrorMessage(err);
|
||||
return;
|
||||
}
|
||||
if (this.hass) {
|
||||
@@ -218,65 +172,24 @@ export class HcMain extends HassElement {
|
||||
}
|
||||
|
||||
private async _handleShowLovelaceMessage(msg: ShowLovelaceViewMessage) {
|
||||
this._showDemo = false;
|
||||
// We should not get this command before we are connected.
|
||||
// Means a client got out of sync. Let's send status to them.
|
||||
if (!this.hass) {
|
||||
this._sendStatus(msg.senderId!);
|
||||
this._error = "Cannot show Lovelace because we're not connected.";
|
||||
this._sendError(
|
||||
ReceiverErrorCode.NOT_CONNECTED,
|
||||
this._error,
|
||||
msg.senderId!
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
(msg.hassUUID && msg.hassUUID !== this._hassUUID) ||
|
||||
(msg.hassUrl && msg.hassUrl !== this.hass.auth.data.hassUrl)
|
||||
) {
|
||||
this._sendStatus(msg.senderId!);
|
||||
this._error =
|
||||
"Cannot show Lovelace because we're not connected to the same Home Assistant instance.";
|
||||
this._sendError(
|
||||
ReceiverErrorCode.WRONG_INSTANCE,
|
||||
this._error,
|
||||
msg.senderId!
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this._error = undefined;
|
||||
if (msg.urlPath === "lovelace") {
|
||||
msg.urlPath = null;
|
||||
}
|
||||
this._lovelacePath = msg.viewPath;
|
||||
if (msg.urlPath === "energy") {
|
||||
this._lovelaceConfig = {
|
||||
views: [
|
||||
{
|
||||
strategy: {
|
||||
type: "energy",
|
||||
options: { show_date_selection: true },
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
this._urlPath = "energy";
|
||||
this._lovelacePath = 0;
|
||||
this._sendStatus();
|
||||
return;
|
||||
}
|
||||
if (!this._unsubLovelace || this._urlPath !== msg.urlPath) {
|
||||
this._urlPath = msg.urlPath;
|
||||
this._lovelaceConfig = undefined;
|
||||
if (this._unsubLovelace) {
|
||||
this._unsubLovelace();
|
||||
}
|
||||
const llColl = atLeastVersion(this.hass.connection.haVersion, 0, 107)
|
||||
? getLovelaceCollection(this.hass.connection, msg.urlPath)
|
||||
: getLegacyLovelaceCollection(this.hass.connection);
|
||||
? getLovelaceCollection(this.hass!.connection, msg.urlPath)
|
||||
: getLegacyLovelaceCollection(this.hass!.connection);
|
||||
// We first do a single refresh because we need to check if there is LL
|
||||
// configuration.
|
||||
try {
|
||||
@@ -284,17 +197,9 @@ export class HcMain extends HassElement {
|
||||
this._unsubLovelace = llColl.subscribe((lovelaceConfig) =>
|
||||
this._handleNewLovelaceConfig(lovelaceConfig)
|
||||
);
|
||||
} catch (err: any) {
|
||||
if (
|
||||
atLeastVersion(this.hass.connection.haVersion, 0, 107) &&
|
||||
err.code !== "config_not_found"
|
||||
) {
|
||||
// eslint-disable-next-line
|
||||
console.log("Error fetching Lovelace configuration", err, msg);
|
||||
this._error = `Error fetching Lovelace configuration: ${err.message}`;
|
||||
this._sendError(ReceiverErrorCode.FETCH_CONFIG_FAILED, this._error);
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line
|
||||
console.log("Error fetching Lovelace configuration", err, msg);
|
||||
// Generate a Lovelace config.
|
||||
this._unsubLovelace = () => undefined;
|
||||
await this._generateLovelaceConfig();
|
||||
@@ -309,27 +214,23 @@ export class HcMain extends HassElement {
|
||||
loadLovelaceResources(resources, this.hass!.auth.data.hassUrl);
|
||||
}
|
||||
}
|
||||
this._showDemo = false;
|
||||
this._lovelacePath = msg.viewPath;
|
||||
|
||||
this._sendStatus();
|
||||
}
|
||||
|
||||
private async _generateLovelaceConfig() {
|
||||
const { generateLovelaceDashboardStrategy } = await import(
|
||||
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||
const { generateLovelaceConfigFromHass } = await import(
|
||||
"../../../../src/panels/lovelace/common/generate-lovelace-config"
|
||||
);
|
||||
this._handleNewLovelaceConfig(
|
||||
await generateLovelaceDashboardStrategy(
|
||||
{
|
||||
hass: this.hass!,
|
||||
narrow: false,
|
||||
},
|
||||
"original-states"
|
||||
)
|
||||
await generateLovelaceConfigFromHass(this.hass!)
|
||||
);
|
||||
}
|
||||
|
||||
private _handleNewLovelaceConfig(lovelaceConfig: LovelaceConfig) {
|
||||
castContext.setApplicationState(lovelaceConfig.title || "");
|
||||
castContext.setApplicationState(lovelaceConfig.title!);
|
||||
this._lovelaceConfig = lovelaceConfig;
|
||||
}
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import "../../../src/resources/ha-style";
|
||||
import "web-animations-js/web-animations-next-lite.min";
|
||||
import "../../../src/resources/roboto";
|
||||
import "../../../src/resources/ha-style";
|
||||
import "./layout/hc-lovelace";
|
||||
|
@@ -6,9 +6,6 @@ set -e
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
export STATS=1
|
||||
statsfile="compilation-stats-demo.json"
|
||||
|
||||
./node_modules/.bin/webpack-cli --profile --node-env=production --json=$statsfile
|
||||
npx webpack-bundle-analyzer $statsfile dist/frontend_latest
|
||||
rm -f $statsfile
|
||||
STATS=1 NODE_ENV=production ../node_modules/.bin/webpack --profile --json > compilation-stats.json
|
||||
npx webpack-bundle-analyzer compilation-stats.json dist/frontend_latest
|
||||
rm compilation-stats.json
|
||||
|
@@ -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": {
|
||||
@@ -246,15 +242,11 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
|
||||
"light.living_room_lights": {
|
||||
entity_id: "light.living_room_lights",
|
||||
state: "on",
|
||||
state: "off",
|
||||
attributes: {
|
||||
min_mireds: 111,
|
||||
max_mireds: 400,
|
||||
brightness: 175,
|
||||
color_temp: 300,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
friendly_name: "Living Room Lights",
|
||||
color_mode: "color_temp",
|
||||
supported_features: 55,
|
||||
},
|
||||
},
|
||||
@@ -267,27 +259,13 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
},
|
||||
"light.kitchen_lights": {
|
||||
entity_id: "light.kitchen_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
min_mireds: 111,
|
||||
max_mireds: 400,
|
||||
brightness: 200,
|
||||
rgb_color: [255, 175, 96],
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "rgb",
|
||||
friendly_name: "Kitchen Lights",
|
||||
supported_features: 55,
|
||||
},
|
||||
},
|
||||
"light.lifx5": {
|
||||
entity_id: "light.lifx5",
|
||||
state: "off",
|
||||
attributes: {
|
||||
supported_color_modes: ["brightness"],
|
||||
friendly_name: "Garage Lights",
|
||||
friendly_name: "Kitchen lights",
|
||||
supported_features: 1,
|
||||
},
|
||||
},
|
||||
|
||||
"sensor.plexspy": {
|
||||
entity_id: "sensor.plexspy",
|
||||
state: "0",
|
||||
@@ -500,6 +478,16 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
icon: "hademo:history",
|
||||
},
|
||||
},
|
||||
"light.lifx5": {
|
||||
entity_id: "light.lifx5",
|
||||
state: "on",
|
||||
attributes: {
|
||||
min_mireds: 111,
|
||||
max_mireds: 400,
|
||||
friendly_name: "Garage lights",
|
||||
supported_features: 55,
|
||||
},
|
||||
},
|
||||
"sensor.alok_to_home": {
|
||||
entity_id: "sensor.alok_to_home",
|
||||
state: "41",
|
||||
@@ -508,7 +496,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
origin_addresses: ["XYZ"],
|
||||
status: "OK",
|
||||
mode: "driving",
|
||||
units: "us_customary",
|
||||
units: "imperial",
|
||||
duration_in_traffic: "41 mins",
|
||||
duration: "44 mins",
|
||||
distance: "34.3 mi",
|
||||
@@ -527,7 +515,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
origin_addresses: ["XYZ"],
|
||||
status: "OK",
|
||||
mode: "driving",
|
||||
units: "us_customary",
|
||||
units: "imperial",
|
||||
duration_in_traffic: "37 mins",
|
||||
duration: "37 mins",
|
||||
distance: "30.2 mi",
|
||||
|
@@ -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",
|
||||
@@ -29,11 +28,6 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Energy distribution today",
|
||||
type: "energy-distribution",
|
||||
link_dashboard: true,
|
||||
},
|
||||
{
|
||||
type: "thermostat",
|
||||
entity: "climate.upstairs",
|
||||
@@ -118,7 +112,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
@@ -200,7 +195,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
@@ -280,7 +276,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
@@ -317,7 +314,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
import { Lovelace } from "../../../src/panels/lovelace/types";
|
||||
import { energyEntities } from "../stubs/entities";
|
||||
import { DemoConfig } from "./types";
|
||||
|
||||
export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
||||
@@ -13,8 +12,9 @@ export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let selectedDemoConfigIndex = 0;
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let selectedDemoConfig: Promise<DemoConfig> =
|
||||
demoConfigs[selectedDemoConfigIndex]();
|
||||
export let selectedDemoConfig: Promise<DemoConfig> = demoConfigs[
|
||||
selectedDemoConfigIndex
|
||||
]();
|
||||
|
||||
export const setDemoConfig = async (
|
||||
hass: MockHomeAssistant,
|
||||
@@ -28,7 +28,6 @@ export const setDemoConfig = async (
|
||||
selectedDemoConfig = confProm;
|
||||
|
||||
hass.addEntities(config.entities(hass.localize), true);
|
||||
hass.addEntities(energyEntities());
|
||||
lovelace.saveConfig(config.lovelace(hass.localize));
|
||||
hass.mockTheme(config.theme());
|
||||
};
|
||||
|
@@ -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: [
|
||||
@@ -194,7 +236,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
||||
type: "state-icon",
|
||||
tap_action: {
|
||||
action: "call-service",
|
||||
data: {
|
||||
service_data: {
|
||||
entity_id: "group.downstairs_lights",
|
||||
},
|
||||
service: "homeassistant.toggle",
|
||||
@@ -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",
|
||||
@@ -1196,7 +1372,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
||||
left: "15%",
|
||||
},
|
||||
type: "state-icon",
|
||||
entity: "binary_sensor.water_leak_sensor_158d00026e26dc",
|
||||
entity: "binary_sensor.water_leak_sensor_158d0002338651",
|
||||
},
|
||||
{
|
||||
prefix: "Kitchen: ",
|
||||
@@ -1206,7 +1382,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
||||
top: "89%",
|
||||
left: "32%",
|
||||
},
|
||||
entity: "binary_sensor.water_leak_sensor_158d00026e26dc",
|
||||
entity: "binary_sensor.water_leak_sensor_158d0002338651",
|
||||
},
|
||||
{
|
||||
style: {
|
||||
@@ -1215,7 +1391,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
||||
left: "60%",
|
||||
},
|
||||
type: "state-icon",
|
||||
entity: "binary_sensor.water_leak_sensor_158d0002338651",
|
||||
entity: "binary_sensor.water_leak_sensor_158d00026e26dc",
|
||||
},
|
||||
{
|
||||
prefix: "Bathroom: ",
|
||||
@@ -1225,7 +1401,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
||||
top: "89%",
|
||||
left: "77%",
|
||||
},
|
||||
entity: "binary_sensor.water_leak_sensor_158d0002338651",
|
||||
entity: "binary_sensor.water_leak_sensor_158d00026e26dc",
|
||||
},
|
||||
],
|
||||
type: "picture-elements",
|
||||
@@ -1235,7 +1411,6 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
||||
type: "vertical-stack",
|
||||
},
|
||||
],
|
||||
path: "security",
|
||||
icon: "hass:shield-home",
|
||||
name: "Security",
|
||||
background:
|
||||
|
@@ -59,7 +59,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
hidden: true,
|
||||
radius: 50,
|
||||
friendly_name: "School",
|
||||
friendly_name: "Skolan",
|
||||
icon: "mdi:school",
|
||||
},
|
||||
},
|
||||
@@ -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",
|
||||
@@ -137,7 +132,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
state: "73",
|
||||
attributes: {
|
||||
unit_of_measurement: "%",
|
||||
friendly_name: "Oskar battery",
|
||||
friendly_name: "oskar batteri",
|
||||
device_class: "battery",
|
||||
},
|
||||
},
|
||||
@@ -146,7 +141,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
state: "88",
|
||||
attributes: {
|
||||
unit_of_measurement: "%",
|
||||
friendly_name: "Bella battery",
|
||||
friendly_name: "bella batteri",
|
||||
device_class: "battery",
|
||||
},
|
||||
},
|
||||
@@ -154,7 +149,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
entity_id: "binary_sensor.unifi_camera",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Motion sensor camera",
|
||||
friendly_name: "R\u00f6relsesensor kamera",
|
||||
icon: "mdi:walk",
|
||||
},
|
||||
},
|
||||
@@ -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",
|
||||
},
|
||||
},
|
||||
@@ -707,7 +702,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
},
|
||||
],
|
||||
cloudiness: 25,
|
||||
friendly_name: "Weather",
|
||||
friendly_name: "V\u00e4der",
|
||||
},
|
||||
},
|
||||
"binary_sensor.ubiquiti_switch": {
|
||||
@@ -731,7 +726,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
round_trip_time_max: "0.626",
|
||||
round_trip_time_mdev: "",
|
||||
round_trip_time_min: "0.358",
|
||||
friendly_name: "Entrance camera",
|
||||
friendly_name: "Entr\u00e9 kamera",
|
||||
device_class: "connectivity",
|
||||
icon: "mdi:cctv",
|
||||
},
|
||||
@@ -797,7 +792,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
battery_level: 34,
|
||||
on: true,
|
||||
friendly_name: "Porch motion sensor",
|
||||
friendly_name: "altan_motion_sensor",
|
||||
device_class: "motion",
|
||||
},
|
||||
},
|
||||
@@ -807,7 +802,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
battery_level: 88,
|
||||
on: true,
|
||||
friendly_name: "Back door sensor",
|
||||
friendly_name: "Altand\u00f6rren sensor",
|
||||
device_class: "opening",
|
||||
icon: "mdi:door",
|
||||
},
|
||||
@@ -818,7 +813,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
battery_level: 74,
|
||||
on: true,
|
||||
friendly_name: "Bathroom motion sensor",
|
||||
friendly_name: "badrumssensor",
|
||||
device_class: "motion",
|
||||
},
|
||||
},
|
||||
@@ -829,7 +824,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
battery_level: 47,
|
||||
on: true,
|
||||
dark: true,
|
||||
friendly_name: "Basement motion sensor",
|
||||
friendly_name: "R\u00f6relsesensor k\u00e4llaren 1",
|
||||
device_class: "motion",
|
||||
icon: "mdi:walk",
|
||||
},
|
||||
@@ -841,7 +836,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
battery_level: 60,
|
||||
on: true,
|
||||
dark: true,
|
||||
friendly_name: "Laundy room motion sensor",
|
||||
friendly_name: "R\u00f6relsesensor tv\u00e4ttstugan",
|
||||
device_class: "motion",
|
||||
icon: "mdi:walk",
|
||||
},
|
||||
@@ -863,7 +858,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
battery_level: 60,
|
||||
on: true,
|
||||
friendly_name: "Pantry motion sensor",
|
||||
friendly_name: "R\u00f6relsesensor skafferiet",
|
||||
device_class: "motion",
|
||||
icon: "mdi:walk",
|
||||
},
|
||||
@@ -875,7 +870,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
battery_level: 60,
|
||||
on: true,
|
||||
dark: true,
|
||||
friendly_name: "Stair motion sensor",
|
||||
friendly_name: "R\u00f6relsesensor k\u00e4llaren 2",
|
||||
device_class: "motion",
|
||||
icon: "mdi:walk",
|
||||
},
|
||||
@@ -887,7 +882,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
battery_level: 47,
|
||||
on: true,
|
||||
dark: true,
|
||||
friendly_name: "Bench sensor",
|
||||
friendly_name: "B\u00e4nksensor",
|
||||
device_class: "motion",
|
||||
},
|
||||
},
|
||||
|
@@ -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",
|
||||
},
|
||||
@@ -277,7 +466,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
||||
],
|
||||
show_header_toggle: false,
|
||||
type: "entities",
|
||||
title: "Bandwidth",
|
||||
title: "Bandbredd",
|
||||
},
|
||||
// {
|
||||
// title: "Updater",
|
||||
|
@@ -980,7 +980,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
icon: "mdi:account-off",
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
templates: {
|
||||
icon: "if (state === 'on') return 'mdi:account'; else if (state === 'off') return 'mdi:account-off';\n",
|
||||
icon:
|
||||
"if (state === 'on') return 'mdi:account'; else if (state === 'off') return 'mdi:account-off';\n",
|
||||
icon_color:
|
||||
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n",
|
||||
},
|
||||
@@ -1004,7 +1005,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
icon: "mdi:account-multiple-minus",
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
templates: {
|
||||
icon: "if (state === 'on') return 'mdi:account-group'; else if (state === 'off') return 'mdi:account-multiple-minus';\n",
|
||||
icon:
|
||||
"if (state === 'on') return 'mdi:account-group'; else if (state === 'off') return 'mdi:account-multiple-minus';\n",
|
||||
icon_color:
|
||||
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n",
|
||||
},
|
||||
@@ -1112,9 +1114,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
min_mireds: 153,
|
||||
max_mireds: 500,
|
||||
brightness: 63,
|
||||
color_temp: 200,
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "color_temp",
|
||||
friendly_name: "Upstairs lights",
|
||||
supported_features: 63,
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
@@ -1126,7 +1125,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
friendly_name: "Walk in closet lights",
|
||||
supported_features: 41,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:wall-sconce",
|
||||
},
|
||||
@@ -1138,8 +1136,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
brightness: 254,
|
||||
friendly_name: "Outdoor lights",
|
||||
supported_features: 41,
|
||||
supported_color_modes: ["brightness"],
|
||||
color_mode: "brightness",
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:wall-sconce",
|
||||
},
|
||||
@@ -1152,8 +1148,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
max_mireds: 500,
|
||||
brightness: 128,
|
||||
color_temp: 366,
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "color_temp",
|
||||
effect_list: ["colorloop"],
|
||||
friendly_name: "Downstairs lights",
|
||||
supported_features: 63,
|
||||
@@ -1313,7 +1307,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
min_mireds: 153,
|
||||
max_mireds: 500,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Bedside Lamp",
|
||||
supported_features: 63,
|
||||
@@ -1327,7 +1320,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
min_mireds: 153,
|
||||
max_mireds: 500,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Floorlamp Reading Light",
|
||||
supported_features: 43,
|
||||
@@ -1343,8 +1335,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
max_mireds: 500,
|
||||
brightness: 128,
|
||||
color_temp: 366,
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "color_temp",
|
||||
effect_list: ["colorloop"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Hallway window light",
|
||||
@@ -1359,7 +1349,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
brightness: 77,
|
||||
is_deconz_group: false,
|
||||
supported_color_modes: ["brightness"],
|
||||
friendly_name: "Isa Ceiling Light",
|
||||
supported_features: 41,
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
@@ -1374,8 +1363,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
max_mireds: 500,
|
||||
brightness: 150,
|
||||
color_temp: 366,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
color_mode: "color_temp",
|
||||
effect_list: ["colorloop"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Floorlamp",
|
||||
@@ -1390,7 +1377,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
friendly_name: "Bedroom Ceiling Light",
|
||||
supported_features: 41,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:ceiling-light",
|
||||
},
|
||||
@@ -1401,7 +1387,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
friendly_name: "Nightlight",
|
||||
supported_features: 17,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:lamp",
|
||||
},
|
||||
@@ -1768,7 +1753,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 2.2,
|
||||
friendly_name: "Upstairs Hallway Light",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:ceiling-light",
|
||||
},
|
||||
@@ -1784,7 +1768,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 0,
|
||||
friendly_name: "Dining Room Light",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:ceiling-light",
|
||||
},
|
||||
@@ -1800,7 +1783,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 0,
|
||||
friendly_name: "Living room Spotlights",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:track-light",
|
||||
},
|
||||
@@ -1817,7 +1799,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 2.5,
|
||||
friendly_name: "Passage Lights",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:track-light",
|
||||
},
|
||||
@@ -1862,7 +1843,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 37.4,
|
||||
friendly_name: "Kitchen Lights",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:track-light",
|
||||
},
|
||||
|
@@ -215,7 +215,6 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
card: {
|
||||
type: "glance",
|
||||
show_state: false,
|
||||
columns: 4,
|
||||
},
|
||||
state_filter: ["on"],
|
||||
},
|
||||
@@ -377,7 +376,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
name: "AC bed",
|
||||
tap_action: {
|
||||
action: "call-service",
|
||||
data: {
|
||||
service_data: {
|
||||
entity_id: "script.air_cleaner_quiet",
|
||||
},
|
||||
service: "script.turn_on",
|
||||
@@ -390,7 +389,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
name: "AC bed",
|
||||
tap_action: {
|
||||
action: "call-service",
|
||||
data: {
|
||||
service_data: {
|
||||
entity_id: "script.air_cleaner_auto",
|
||||
},
|
||||
service: "script.turn_on",
|
||||
@@ -403,7 +402,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
name: "AC bed",
|
||||
tap_action: {
|
||||
action: "call-service",
|
||||
data: {
|
||||
service_data: {
|
||||
entity_id: "script.air_cleaner_turbo",
|
||||
},
|
||||
service: "script.turn_on",
|
||||
@@ -416,7 +415,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
name: "AC",
|
||||
tap_action: {
|
||||
action: "call-service",
|
||||
data: {
|
||||
service_data: {
|
||||
entity_id: "script.ac_off",
|
||||
},
|
||||
service: "script.turn_on",
|
||||
@@ -429,7 +428,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
name: "AC",
|
||||
tap_action: {
|
||||
action: "call-service",
|
||||
data: {
|
||||
service_data: {
|
||||
entity_id: "script.ac_on",
|
||||
},
|
||||
service: "script.turn_on",
|
||||
@@ -440,43 +439,57 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
type: "horizontal-stack",
|
||||
},
|
||||
{
|
||||
type: "grid",
|
||||
columns: 2,
|
||||
cards: [
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_bedroom",
|
||||
cards: [
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_bedroom",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "S's room",
|
||||
entity: "sensor.temperature_stefan",
|
||||
},
|
||||
],
|
||||
type: "horizontal-stack",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "S's room",
|
||||
entity: "sensor.temperature_stefan",
|
||||
cards: [
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_passage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Bathroom",
|
||||
entity: "sensor.temperature_downstairs_bathroom",
|
||||
},
|
||||
],
|
||||
type: "horizontal-stack",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_passage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Bathroom",
|
||||
entity: "sensor.temperature_downstairs_bathroom",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_storage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Refrigerator",
|
||||
entity: "sensor.refrigerator",
|
||||
cards: [
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_storage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Refrigerator",
|
||||
entity: "sensor.refrigerator",
|
||||
},
|
||||
],
|
||||
type: "horizontal-stack",
|
||||
},
|
||||
],
|
||||
type: "vertical-stack",
|
||||
},
|
||||
{
|
||||
entities: [
|
||||
@@ -629,7 +642,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
entity: "scene.morning_lights",
|
||||
tap_action: {
|
||||
action: "call-service",
|
||||
data: {
|
||||
service_data: {
|
||||
entity_id: "scene.morning_lights",
|
||||
},
|
||||
service: "scene.turn_on",
|
||||
@@ -641,7 +654,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
entity: "scene.movie_time",
|
||||
tap_action: {
|
||||
action: "call-service",
|
||||
data: {
|
||||
service_data: {
|
||||
entity_id: "scene.movie_time",
|
||||
},
|
||||
service: "scene.turn_on",
|
||||
@@ -702,7 +715,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
entity: "light.downstairs_lights",
|
||||
tap_action: {
|
||||
action: "call-service",
|
||||
data: {
|
||||
service_data: {
|
||||
entity_id: "light.downstairs_lights",
|
||||
},
|
||||
service: "light.toggle",
|
||||
@@ -714,7 +727,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
entity: "light.upstairs_lights",
|
||||
tap_action: {
|
||||
action: "call-service",
|
||||
data: {
|
||||
service_data: {
|
||||
entity_id: "light.upstairs_lights",
|
||||
},
|
||||
service: "light.toggle",
|
||||
@@ -795,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",
|
||||
@@ -844,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",
|
||||
@@ -862,7 +940,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
"sensor.system_ring_chime",
|
||||
],
|
||||
type: "glance",
|
||||
columns: 4,
|
||||
columns: 5,
|
||||
show_state: false,
|
||||
},
|
||||
{
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable */
|
||||
import { LitElement } from "lit";
|
||||
import { LitElement } from "lit-element";
|
||||
import "./card-tools";
|
||||
|
||||
class CardModder extends LitElement {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable */
|
||||
import { html, LitElement } from "lit";
|
||||
import { html, LitElement } from "lit-element";
|
||||
|
||||
if (!window.cardTools) {
|
||||
const version = 0.2;
|
||||
@@ -138,7 +138,7 @@ if (!window.cardTools) {
|
||||
return cardTools.createThing("row", config);
|
||||
|
||||
const domain = config.entity.split(".", 1)[0];
|
||||
Object.assign(config, { type: DEFAULT_ROWS[domain] || "simple" });
|
||||
Object.assign(config, { type: DEFAULT_ROWS[domain] || "text" });
|
||||
return cardTools.createThing("entity-row", config);
|
||||
};
|
||||
|
||||
|
@@ -1,6 +1,12 @@
|
||||
import { mdiTelevision } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { CastManager } from "../../../src/cast/cast_manager";
|
||||
import { castSendShowDemo } from "../../../src/cast/receiver_messages";
|
||||
import "../../../src/components/ha-icon";
|
||||
@@ -14,21 +20,21 @@ import { HomeAssistant } from "../../../src/types";
|
||||
class CastDemoRow extends LitElement implements LovelaceRow {
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
@state() private _castManager?: CastManager | null;
|
||||
@internalProperty() private _castManager?: CastManager | null;
|
||||
|
||||
public setConfig(_config: CastConfig): void {
|
||||
// No config possible.
|
||||
}
|
||||
|
||||
protected render() {
|
||||
protected render(): TemplateResult {
|
||||
if (
|
||||
!this._castManager ||
|
||||
this._castManager.castState === "NO_DEVICES_AVAILABLE"
|
||||
) {
|
||||
return nothing;
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-svg-icon .path=${mdiTelevision}></ha-svg-icon>
|
||||
<ha-icon icon="hademo:television"></ha-icon>
|
||||
<div class="flex">
|
||||
<div class="name">Show Chromecast interface</div>
|
||||
<google-cast-launcher></google-cast-launcher>
|
||||
@@ -67,13 +73,13 @@ class CastDemoRow extends LitElement implements LovelaceRow {
|
||||
this.style.display = this._castManager ? "" : "none";
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
ha-svg-icon {
|
||||
ha-icon {
|
||||
padding: 8px;
|
||||
color: var(--paper-item-icon-color);
|
||||
}
|
||||
|
@@ -1,7 +1,14 @@
|
||||
import "@material/mwc-button";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { until } from "lit/directives/until";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/ha-circular-progress";
|
||||
import { LovelaceCardConfig } from "../../../src/data/lovelace";
|
||||
@@ -14,13 +21,12 @@ import {
|
||||
setDemoConfig,
|
||||
} from "../configs/demo-configs";
|
||||
|
||||
@customElement("ha-demo-card")
|
||||
export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
@property({ attribute: false }) public lovelace?: Lovelace;
|
||||
|
||||
@property({ attribute: false }) public hass!: MockHomeAssistant;
|
||||
|
||||
@state() private _switching = false;
|
||||
@internalProperty() private _switching?: boolean;
|
||||
|
||||
private _hidden = localStorage.hide_demo_card;
|
||||
|
||||
@@ -28,11 +34,16 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
return this._hidden ? 0 : 2;
|
||||
}
|
||||
|
||||
public setConfig(_config: LovelaceCardConfig) {}
|
||||
public setConfig(
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
config: LovelaceCardConfig
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
) {}
|
||||
|
||||
protected render() {
|
||||
protected render(): TemplateResult {
|
||||
if (this._hidden) {
|
||||
return nothing;
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-card>
|
||||
@@ -45,7 +56,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
(conf) => html`
|
||||
${conf.name}
|
||||
<small>
|
||||
<a target="_blank" href=${conf.authorUrl}>
|
||||
<a target="_blank" href="${conf.authorUrl}">
|
||||
${this.hass.localize(
|
||||
"ui.panel.page-demo.cards.demo.demo_by",
|
||||
"name",
|
||||
@@ -95,14 +106,14 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
this._switching = true;
|
||||
try {
|
||||
await setDemoConfig(this.hass, this.lovelace!, index);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
alert("Failed to switch config :-(");
|
||||
} finally {
|
||||
this._switching = false;
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
css`
|
||||
a {
|
||||
@@ -155,3 +166,5 @@ declare global {
|
||||
"ha-demo-card": HADemoCard;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-demo-card", HADemoCard);
|
||||
|
@@ -1,4 +1,11 @@
|
||||
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 */
|
||||
setTimeout(() => {
|
||||
import("web-animations-js/web-animations-next-lite.min");
|
||||
}, 1000);
|
||||
|
@@ -1,6 +1,4 @@
|
||||
// Compat needs to be first import
|
||||
import "../../src/resources/compatibility";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { isNavigationClick } from "../../src/common/dom/is-navigation-click";
|
||||
import { navigate } from "../../src/common/navigate";
|
||||
import {
|
||||
@@ -11,27 +9,21 @@ import { HomeAssistantAppEl } from "../../src/layouts/home-assistant";
|
||||
import { HomeAssistant } from "../../src/types";
|
||||
import { selectedDemoConfig } from "./configs/demo-configs";
|
||||
import { mockAuth } from "./stubs/auth";
|
||||
import { mockConfigEntries } from "./stubs/config_entries";
|
||||
import { mockEnergy } from "./stubs/energy";
|
||||
import { energyEntities } from "./stubs/entities";
|
||||
import { mockEntityRegistry } from "./stubs/entity_registry";
|
||||
import { mockEvents } from "./stubs/events";
|
||||
import { mockFrontend } from "./stubs/frontend";
|
||||
import { mockHistory } from "./stubs/history";
|
||||
import { mockLovelace } from "./stubs/lovelace";
|
||||
import { mockMediaPlayer } from "./stubs/media_player";
|
||||
import { mockPersistentNotification } from "./stubs/persistent_notification";
|
||||
import { mockRecorder } from "./stubs/recorder";
|
||||
import { mockShoppingList } from "./stubs/shopping_list";
|
||||
import { mockSystemLog } from "./stubs/system_log";
|
||||
import { mockTemplate } from "./stubs/template";
|
||||
import { mockTranslations } from "./stubs/translations";
|
||||
|
||||
@customElement("ha-demo")
|
||||
export class HaDemo extends HomeAssistantAppEl {
|
||||
protected async _initializeHass() {
|
||||
class HaDemo extends HomeAssistantAppEl {
|
||||
protected async _initialize() {
|
||||
const initial: Partial<MockHomeAssistant> = {
|
||||
panelUrl: (this as any)._panelUrl,
|
||||
panelUrl: (this as any).panelUrl,
|
||||
// Override updateHass so that the correct hass lifecycle methods are called
|
||||
updateHass: (hassUpdate: Partial<HomeAssistant>) =>
|
||||
this._updateHass(hassUpdate),
|
||||
@@ -48,52 +40,13 @@ export class HaDemo extends HomeAssistantAppEl {
|
||||
mockAuth(hass);
|
||||
mockTranslations(hass);
|
||||
mockHistory(hass);
|
||||
mockRecorder(hass);
|
||||
mockShoppingList(hass);
|
||||
mockSystemLog(hass);
|
||||
mockTemplate(hass);
|
||||
mockEvents(hass);
|
||||
mockMediaPlayer(hass);
|
||||
mockFrontend(hass);
|
||||
mockEnergy(hass);
|
||||
mockPersistentNotification(hass);
|
||||
mockConfigEntries(hass);
|
||||
mockEntityRegistry(hass, [
|
||||
{
|
||||
config_entry_id: "co2signal",
|
||||
device_id: "co2signal",
|
||||
area_id: null,
|
||||
disabled_by: null,
|
||||
entity_id: "sensor.co2_intensity",
|
||||
id: "sensor.co2_intensity",
|
||||
name: null,
|
||||
icon: null,
|
||||
platform: "co2signal",
|
||||
hidden_by: null,
|
||||
entity_category: null,
|
||||
has_entity_name: false,
|
||||
unique_id: "co2_intensity",
|
||||
options: null,
|
||||
},
|
||||
{
|
||||
config_entry_id: "co2signal",
|
||||
device_id: "co2signal",
|
||||
area_id: null,
|
||||
disabled_by: null,
|
||||
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||
id: "sensor.co2_intensity",
|
||||
name: null,
|
||||
icon: null,
|
||||
platform: "co2signal",
|
||||
hidden_by: null,
|
||||
entity_category: null,
|
||||
has_entity_name: false,
|
||||
unique_id: "grid_fossil_fuel_percentage",
|
||||
options: null,
|
||||
},
|
||||
]);
|
||||
|
||||
hass.addEntities(energyEntities());
|
||||
|
||||
// Once config is loaded AND localize, set entities and apply theme.
|
||||
Promise.all([selectedDemoConfig, localizePromise]).then(
|
||||
@@ -116,7 +69,7 @@ export class HaDemo extends HomeAssistantAppEl {
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
navigate(href);
|
||||
navigate(this, href);
|
||||
},
|
||||
{ capture: true }
|
||||
);
|
||||
@@ -125,8 +78,4 @@ export class HaDemo extends HomeAssistantAppEl {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-demo": HaDemo;
|
||||
}
|
||||
}
|
||||
customElements.define("ha-demo", HaDemo);
|
||||
|
File diff suppressed because one or more lines are too long
@@ -1,7 +0,0 @@
|
||||
import { AreaRegistryEntry } from "../../../src/data/area_registry";
|
||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockAreaRegistry = (
|
||||
hass: MockHomeAssistant,
|
||||
data: AreaRegistryEntry[] = []
|
||||
) => hass.mockWS("config/area_registry/list", () => data);
|
@@ -1,20 +0,0 @@
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockConfigEntries = (hass: MockHomeAssistant) => {
|
||||
hass.mockWS("config_entries/get", () => [
|
||||
{
|
||||
entry_id: "co2signal",
|
||||
domain: "co2signal",
|
||||
title: "CO2 Signal",
|
||||
source: "user",
|
||||
state: "loaded",
|
||||
supports_options: false,
|
||||
supports_remove_device: false,
|
||||
supports_unload: true,
|
||||
pref_disable_new_entities: false,
|
||||
pref_disable_polling: false,
|
||||
disabled_by: null,
|
||||
reason: null,
|
||||
},
|
||||
]);
|
||||
};
|
@@ -1,7 +0,0 @@
|
||||
import { DeviceRegistryEntry } from "../../../src/data/device_registry";
|
||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockDeviceRegistry = (
|
||||
hass: MockHomeAssistant,
|
||||
data: DeviceRegistryEntry[] = []
|
||||
) => hass.mockWS("config/device_registry/list", () => data);
|
@@ -1,148 +0,0 @@
|
||||
import { format, startOfToday, startOfTomorrow } from "date-fns/esm";
|
||||
import {
|
||||
EnergyInfo,
|
||||
EnergyPreferences,
|
||||
EnergySolarForecasts,
|
||||
FossilEnergyConsumption,
|
||||
} from "../../../src/data/energy";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockEnergy = (hass: MockHomeAssistant) => {
|
||||
hass.mockWS(
|
||||
"energy/get_prefs",
|
||||
(): EnergyPreferences => ({
|
||||
energy_sources: [
|
||||
{
|
||||
type: "grid",
|
||||
flow_from: [
|
||||
{
|
||||
stat_energy_from: "sensor.energy_consumption_tarif_1",
|
||||
stat_cost: "sensor.energy_consumption_tarif_1_cost",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
{
|
||||
stat_energy_from: "sensor.energy_consumption_tarif_2",
|
||||
stat_cost: "sensor.energy_consumption_tarif_2_cost",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
],
|
||||
flow_to: [
|
||||
{
|
||||
stat_energy_to: "sensor.energy_production_tarif_1",
|
||||
stat_compensation:
|
||||
"sensor.energy_production_tarif_1_compensation",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
{
|
||||
stat_energy_to: "sensor.energy_production_tarif_2",
|
||||
stat_compensation:
|
||||
"sensor.energy_production_tarif_2_compensation",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
],
|
||||
cost_adjustment_day: 0,
|
||||
},
|
||||
{
|
||||
type: "solar",
|
||||
stat_energy_from: "sensor.solar_production",
|
||||
config_entry_solar_forecast: ["solar_forecast"],
|
||||
},
|
||||
/* {
|
||||
type: "battery",
|
||||
stat_energy_from: "sensor.battery_output",
|
||||
stat_energy_to: "sensor.battery_input",
|
||||
}, */
|
||||
{
|
||||
type: "gas",
|
||||
stat_energy_from: "sensor.energy_gas",
|
||||
stat_cost: "sensor.energy_gas_cost",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
],
|
||||
device_consumption: [
|
||||
{
|
||||
stat_consumption: "sensor.energy_car",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_ac",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_washing_machine",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_dryer",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_heat_pump",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_boiler",
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
hass.mockWS(
|
||||
"energy/info",
|
||||
(): EnergyInfo => ({ cost_sensors: {}, solar_forecast_domains: [] })
|
||||
);
|
||||
hass.mockWS(
|
||||
"energy/fossil_energy_consumption",
|
||||
({ period }): FossilEnergyConsumption => ({
|
||||
start: period === "month" ? 250 : period === "day" ? 10 : 2,
|
||||
})
|
||||
);
|
||||
const todayString = format(startOfToday(), "yyyy-MM-dd");
|
||||
const tomorrowString = format(startOfTomorrow(), "yyyy-MM-dd");
|
||||
hass.mockWS(
|
||||
"energy/solar_forecast",
|
||||
(): EnergySolarForecasts => ({
|
||||
solar_forecast: {
|
||||
wh_hours: {
|
||||
[`${todayString}T06:00:00`]: 0,
|
||||
[`${todayString}T06:23:00`]: 6,
|
||||
[`${todayString}T06:45:00`]: 39,
|
||||
[`${todayString}T07:00:00`]: 28,
|
||||
[`${todayString}T08:00:00`]: 208,
|
||||
[`${todayString}T09:00:00`]: 352,
|
||||
[`${todayString}T10:00:00`]: 544,
|
||||
[`${todayString}T11:00:00`]: 748,
|
||||
[`${todayString}T12:00:00`]: 1259,
|
||||
[`${todayString}T13:00:00`]: 1361,
|
||||
[`${todayString}T14:00:00`]: 1373,
|
||||
[`${todayString}T15:00:00`]: 1370,
|
||||
[`${todayString}T16:00:00`]: 1186,
|
||||
[`${todayString}T17:00:00`]: 937,
|
||||
[`${todayString}T18:00:00`]: 652,
|
||||
[`${todayString}T19:00:00`]: 370,
|
||||
[`${todayString}T20:00:00`]: 155,
|
||||
[`${todayString}T21:48:00`]: 24,
|
||||
[`${todayString}T22:36:00`]: 0,
|
||||
[`${tomorrowString}T06:01:00`]: 0,
|
||||
[`${tomorrowString}T06:23:00`]: 9,
|
||||
[`${tomorrowString}T06:45:00`]: 47,
|
||||
[`${tomorrowString}T07:00:00`]: 48,
|
||||
[`${tomorrowString}T08:00:00`]: 473,
|
||||
[`${tomorrowString}T09:00:00`]: 827,
|
||||
[`${tomorrowString}T10:00:00`]: 1153,
|
||||
[`${tomorrowString}T11:00:00`]: 1413,
|
||||
[`${tomorrowString}T12:00:00`]: 1590,
|
||||
[`${tomorrowString}T13:00:00`]: 1652,
|
||||
[`${tomorrowString}T14:00:00`]: 1612,
|
||||
[`${tomorrowString}T15:00:00`]: 1438,
|
||||
[`${tomorrowString}T16:00:00`]: 1149,
|
||||
[`${tomorrowString}T17:00:00`]: 830,
|
||||
[`${tomorrowString}T18:00:00`]: 542,
|
||||
[`${tomorrowString}T19:00:00`]: 311,
|
||||
[`${tomorrowString}T20:00:00`]: 140,
|
||||
[`${tomorrowString}T21:47:00`]: 22,
|
||||
[`${tomorrowString}T22:34:00`]: 0,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
@@ -1,178 +0,0 @@
|
||||
import { convertEntities } from "../../../src/fake_data/entity";
|
||||
|
||||
export const energyEntities = () =>
|
||||
convertEntities({
|
||||
"sensor.grid_fossil_fuel_percentage": {
|
||||
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
"sensor.solar_production": {
|
||||
entity_id: "sensor.solar_production",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Solar",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.battery_input": {
|
||||
entity_id: "sensor.battery_input",
|
||||
state: "4",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Battery Input",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.battery_output": {
|
||||
entity_id: "sensor.battery_output",
|
||||
state: "3",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Battery Output",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_1": {
|
||||
entity_id: "sensor.energy_consumption_tarif_1 ",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Grid consumption low tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_2": {
|
||||
entity_id: "sensor.energy_consumption_tarif_2",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Grid consumption high tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_1": {
|
||||
entity_id: "sensor.energy_production_tarif_1",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Returned to grid low tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_2": {
|
||||
entity_id: "sensor.energy_production_tarif_2",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Returned to grid high tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_1_cost": {
|
||||
entity_id: "sensor.energy_consumption_tarif_1_cost",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_2_cost": {
|
||||
entity_id: "sensor.energy_consumption_tarif_2_cost",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_1_compensation": {
|
||||
entity_id: "sensor.energy_production_tarif_1_compensation",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_2_compensation": {
|
||||
entity_id: "sensor.energy_production_tarif_2_compensation",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_gas_cost": {
|
||||
entity_id: "sensor.energy_gas_cost",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_gas": {
|
||||
entity_id: "sensor.energy_gas",
|
||||
state: "4",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Gas",
|
||||
unit_of_measurement: "m³",
|
||||
},
|
||||
},
|
||||
"sensor.energy_car": {
|
||||
entity_id: "sensor.energy_car",
|
||||
state: "4",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Electric car",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_ac": {
|
||||
entity_id: "sensor.energy_ac",
|
||||
state: "3",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Air conditioning",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_washing_machine": {
|
||||
entity_id: "sensor.energy_washing_machine",
|
||||
state: "6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Washing machine",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_dryer": {
|
||||
entity_id: "sensor.energy_dryer",
|
||||
state: "5.5",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Dryer",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_heat_pump": {
|
||||
entity_id: "sensor.energy_heat_pump",
|
||||
state: "6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Heat pump",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_boiler": {
|
||||
entity_id: "sensor.energy_boiler",
|
||||
state: "7",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Boiler",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
});
|
@@ -1,9 +0,0 @@
|
||||
import { EntityRegistryEntry } from "../../../src/data/entity_registry";
|
||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockEntityRegistry = (
|
||||
hass: MockHomeAssistant,
|
||||
data: EntityRegistryEntry[] = []
|
||||
) => {
|
||||
hass.mockWS("config/entity_registry/list", () => data);
|
||||
};
|
@@ -1,59 +0,0 @@
|
||||
import { HassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
|
||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockHassioSupervisor = (hass: MockHomeAssistant) => {
|
||||
hass.config.components.push("hassio");
|
||||
hass.mockWS("supervisor/api", (msg) => {
|
||||
if (msg.endpoint === "/supervisor/info") {
|
||||
const data: HassioSupervisorInfo = {
|
||||
version: "2021.10.dev0805",
|
||||
version_latest: "2021.10.dev0806",
|
||||
update_available: true,
|
||||
channel: "dev",
|
||||
arch: "aarch64",
|
||||
supported: true,
|
||||
healthy: true,
|
||||
ip_address: "172.30.32.2",
|
||||
wait_boot: 5,
|
||||
timezone: "America/Los_Angeles",
|
||||
logging: "info",
|
||||
debug: false,
|
||||
debug_block: false,
|
||||
diagnostics: true,
|
||||
addons: [
|
||||
{
|
||||
name: "Visual Studio Code",
|
||||
slug: "a0d7b954_vscode",
|
||||
description:
|
||||
"Fully featured VSCode experience, to edit your HA config in the browser, including auto-completion!",
|
||||
state: "started",
|
||||
version: "3.6.2",
|
||||
version_latest: "3.6.2",
|
||||
update_available: false,
|
||||
repository: "a0d7b954",
|
||||
icon: false,
|
||||
logo: true,
|
||||
},
|
||||
{
|
||||
name: "Z-Wave JS",
|
||||
slug: "core_zwave_js",
|
||||
description:
|
||||
"Control a ZWave network with Home Assistant Z-Wave JS",
|
||||
state: "started",
|
||||
version: "0.1.45",
|
||||
version_latest: "0.1.45",
|
||||
update_available: false,
|
||||
repository: "core",
|
||||
icon: true,
|
||||
logo: true,
|
||||
},
|
||||
] as any,
|
||||
addons_repositories: [
|
||||
"https://github.com/hassio-addons/repository",
|
||||
] as any,
|
||||
};
|
||||
return data;
|
||||
}
|
||||
return Promise.reject(`${msg.method} ${msg.endpoint} is not implemented`);
|
||||
});
|
||||
};
|
@@ -66,8 +66,15 @@ const incrementalUnits = ["clients", "queries", "ads"];
|
||||
|
||||
export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||
mockHass.mockAPI(
|
||||
/history\/period\/.+/,
|
||||
(hass, _method, path, _parameters) => {
|
||||
new RegExp("history/period/.+"),
|
||||
(
|
||||
hass,
|
||||
// @ts-ignore
|
||||
method,
|
||||
path,
|
||||
// @ts-ignore
|
||||
parameters
|
||||
) => {
|
||||
const params = parseQuery<HistoryQueryParams>(path.split("?")[1]);
|
||||
const entities = params.filter_entity_id.split(",");
|
||||
|
||||
@@ -88,7 +95,7 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||
const numberState = Number(state.state);
|
||||
|
||||
if (isNaN(numberState)) {
|
||||
// eslint-disable-next-line no-console
|
||||
// eslint-disable-next-line
|
||||
console.log(
|
||||
"Ignoring state with unparsable state but with a unit",
|
||||
entityId,
|
||||
|
@@ -10,9 +10,10 @@ export const mockLovelace = (
|
||||
localizePromise: Promise<LocalizeFunc>
|
||||
) => {
|
||||
hass.mockWS("lovelace/config", () =>
|
||||
Promise.all([selectedDemoConfig, localizePromise]).then(
|
||||
([config, localize]) => config.lovelace(localize)
|
||||
)
|
||||
Promise.all([
|
||||
selectedDemoConfig,
|
||||
localizePromise,
|
||||
]).then(([config, localize]) => config.lovelace(localize))
|
||||
);
|
||||
|
||||
hass.mockWS("lovelace/config/save", () => Promise.resolve());
|
||||
@@ -23,9 +24,9 @@ customElements.whenDefined("hui-view").then(() => {
|
||||
// eslint-disable-next-line
|
||||
const HUIView = customElements.get("hui-view");
|
||||
// Patch HUI-VIEW to make the lovelace object available to the demo card
|
||||
const oldCreateCard = HUIView!.prototype.createCardElement;
|
||||
const oldCreateCard = HUIView.prototype.createCardElement;
|
||||
|
||||
HUIView!.prototype.createCardElement = function (config) {
|
||||
HUIView.prototype.createCardElement = function (config) {
|
||||
const el = oldCreateCard.call(this, config);
|
||||
if (el.tagName === "HA-DEMO-CARD") {
|
||||
(el as HADemoCard).lovelace = this.lovelace;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user