Compare commits

..

7 Commits

Author SHA1 Message Date
Joakim Sørensen
f69bce534a Update src/dialogs/analytics/dialog-analytics-optin.ts
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-04-27 16:30:52 +02:00
Joakim Sørensen
575f58bd88 Update src/dialogs/analytics/dialog-analytics-optin.ts
Co-authored-by: Charles Garwood <cgarwood@gmail.com>
2021-04-27 15:28:54 +02:00
Ludeeus
35535628fc reword 2021-04-27 11:33:39 +00:00
Ludeeus
8e018c9cfe add anonymized word 2021-04-27 11:23:53 +00:00
Ludeeus
5ae268b792 add analyticsLearnMore 2021-04-27 11:09:02 +00:00
Ludeeus
329732ac30 change button wording 2021-04-27 11:08:24 +00:00
Ludeeus
7f88bab552 Add analytics dialog 2021-04-27 11:06:25 +00:00
332 changed files with 4683 additions and 6286 deletions

View File

@@ -4,7 +4,8 @@
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
"plugin:wc/recommended", "plugin:wc/recommended",
"plugin:lit/recommended", "plugin:lit/recommended",
"prettier" "prettier",
"prettier/@typescript-eslint"
], ],
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"parserOptions": { "parserOptions": {
@@ -84,25 +85,6 @@
"@typescript-eslint/explicit-function-return-type": 0, "@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/explicit-module-boundary-types": 0, "@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/no-shadow": ["error"], "@typescript-eslint/no-shadow": ["error"],
"@typescript-eslint/naming-convention": [
0,
{
"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"]
}
],
"lit/attribute-value-entities": 0 "lit/attribute-value-entities": 0
}, },
"plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"], "plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],

View File

@@ -37,11 +37,9 @@ jobs:
- name: Build resources - name: Build resources
run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos
- name: Run eslint - name: Run eslint
run: yarn run lint:eslint run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore
- name: Run tsc - name: Run tsc
run: yarn run lint:types run: ./node_modules/.bin/tsc
- name: Run prettier
run: yarn run lint:prettier
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@@ -54,7 +54,9 @@ class HcCast extends LitElement {
const error = const error =
this.castManager.castState === "NO_DEVICES_AVAILABLE" this.castManager.castState === "NO_DEVICES_AVAILABLE"
? html` ? 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; : undefined;

View File

@@ -86,7 +86,9 @@ export class HcConnect extends LitElement {
</div> </div>
<div class="card-actions"> <div class="card-actions">
<a href="/"> <a href="/">
<mwc-button> Retry </mwc-button> <mwc-button>
Retry
</mwc-button>
</a> </a>
<div class="spacer"></div> <div class="spacer"></div>
<mwc-button @click=${this._handleLogout}>Log out</mwc-button> <mwc-button @click=${this._handleLogout}>Log out</mwc-button>

View File

@@ -246,15 +246,11 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
"light.living_room_lights": { "light.living_room_lights": {
entity_id: "light.living_room_lights", entity_id: "light.living_room_lights",
state: "on", state: "off",
attributes: { attributes: {
min_mireds: 111, min_mireds: 111,
max_mireds: 400, max_mireds: 400,
brightness: 175,
color_temp: 300,
supported_color_modes: ["brightness", "color_temp"],
friendly_name: "Living Room Lights", friendly_name: "Living Room Lights",
color_mode: "color_temp",
supported_features: 55, supported_features: 55,
}, },
}, },
@@ -267,27 +263,13 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
}, },
"light.kitchen_lights": { "light.kitchen_lights": {
entity_id: "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", state: "off",
attributes: { attributes: {
supported_color_modes: ["brightness"], friendly_name: "Kitchen Lights",
friendly_name: "Garage Lights",
supported_features: 1, supported_features: 1,
}, },
}, },
"sensor.plexspy": { "sensor.plexspy": {
entity_id: "sensor.plexspy", entity_id: "sensor.plexspy",
state: "0", state: "0",
@@ -500,6 +482,16 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
icon: "hademo:history", 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": { "sensor.alok_to_home": {
entity_id: "sensor.alok_to_home", entity_id: "sensor.alok_to_home",
state: "41", state: "41",

View File

@@ -1114,9 +1114,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
min_mireds: 153, min_mireds: 153,
max_mireds: 500, max_mireds: 500,
brightness: 63, brightness: 63,
color_temp: 200,
supported_color_modes: ["brightness", "color_temp", "rgb"],
color_mode: "color_temp",
friendly_name: "Upstairs lights", friendly_name: "Upstairs lights",
supported_features: 63, supported_features: 63,
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
@@ -1128,7 +1125,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: { attributes: {
friendly_name: "Walk in closet lights", friendly_name: "Walk in closet lights",
supported_features: 41, supported_features: 41,
supported_color_modes: ["brightness", "color_temp"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:wall-sconce", icon: "mdi:wall-sconce",
}, },
@@ -1140,8 +1136,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
brightness: 254, brightness: 254,
friendly_name: "Outdoor lights", friendly_name: "Outdoor lights",
supported_features: 41, supported_features: 41,
supported_color_modes: ["brightness"],
color_mode: "brightness",
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:wall-sconce", icon: "mdi:wall-sconce",
}, },
@@ -1154,8 +1148,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
max_mireds: 500, max_mireds: 500,
brightness: 128, brightness: 128,
color_temp: 366, color_temp: 366,
supported_color_modes: ["brightness", "color_temp", "rgb"],
color_mode: "color_temp",
effect_list: ["colorloop"], effect_list: ["colorloop"],
friendly_name: "Downstairs lights", friendly_name: "Downstairs lights",
supported_features: 63, supported_features: 63,
@@ -1315,7 +1307,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: { attributes: {
min_mireds: 153, min_mireds: 153,
max_mireds: 500, max_mireds: 500,
supported_color_modes: ["brightness", "color_temp"],
is_deconz_group: false, is_deconz_group: false,
friendly_name: "Bedside Lamp", friendly_name: "Bedside Lamp",
supported_features: 63, supported_features: 63,
@@ -1329,7 +1320,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: { attributes: {
min_mireds: 153, min_mireds: 153,
max_mireds: 500, max_mireds: 500,
supported_color_modes: ["brightness", "color_temp"],
is_deconz_group: false, is_deconz_group: false,
friendly_name: "Floorlamp Reading Light", friendly_name: "Floorlamp Reading Light",
supported_features: 43, supported_features: 43,
@@ -1345,8 +1335,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
max_mireds: 500, max_mireds: 500,
brightness: 128, brightness: 128,
color_temp: 366, color_temp: 366,
supported_color_modes: ["brightness", "color_temp", "rgb"],
color_mode: "color_temp",
effect_list: ["colorloop"], effect_list: ["colorloop"],
is_deconz_group: false, is_deconz_group: false,
friendly_name: "Hallway window light", friendly_name: "Hallway window light",
@@ -1361,7 +1349,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: { attributes: {
brightness: 77, brightness: 77,
is_deconz_group: false, is_deconz_group: false,
supported_color_modes: ["brightness"],
friendly_name: "Isa Ceiling Light", friendly_name: "Isa Ceiling Light",
supported_features: 41, supported_features: 41,
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
@@ -1376,8 +1363,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
max_mireds: 500, max_mireds: 500,
brightness: 150, brightness: 150,
color_temp: 366, color_temp: 366,
supported_color_modes: ["brightness", "color_temp"],
color_mode: "color_temp",
effect_list: ["colorloop"], effect_list: ["colorloop"],
is_deconz_group: false, is_deconz_group: false,
friendly_name: "Floorlamp", friendly_name: "Floorlamp",
@@ -1392,7 +1377,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: { attributes: {
friendly_name: "Bedroom Ceiling Light", friendly_name: "Bedroom Ceiling Light",
supported_features: 41, supported_features: 41,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:ceiling-light", icon: "mdi:ceiling-light",
}, },
@@ -1403,7 +1387,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: { attributes: {
friendly_name: "Nightlight", friendly_name: "Nightlight",
supported_features: 17, supported_features: 17,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:lamp", icon: "mdi:lamp",
}, },
@@ -1770,7 +1753,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 2.2, power_consumption: 2.2,
friendly_name: "Upstairs Hallway Light", friendly_name: "Upstairs Hallway Light",
supported_features: 33, supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:ceiling-light", icon: "mdi:ceiling-light",
}, },
@@ -1786,7 +1768,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 0, power_consumption: 0,
friendly_name: "Dining Room Light", friendly_name: "Dining Room Light",
supported_features: 33, supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:ceiling-light", icon: "mdi:ceiling-light",
}, },
@@ -1802,7 +1783,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 0, power_consumption: 0,
friendly_name: "Living room Spotlights", friendly_name: "Living room Spotlights",
supported_features: 33, supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:track-light", icon: "mdi:track-light",
}, },
@@ -1819,7 +1799,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 2.5, power_consumption: 2.5,
friendly_name: "Passage Lights", friendly_name: "Passage Lights",
supported_features: 33, supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:track-light", icon: "mdi:track-light",
}, },
@@ -1864,7 +1843,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 37.4, power_consumption: 37.4,
friendly_name: "Kitchen Lights", friendly_name: "Kitchen Lights",
supported_features: 33, supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:track-light", icon: "mdi:track-light",
}, },

View File

@@ -3,6 +3,8 @@ import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockTranslations = (hass: MockHomeAssistant) => { export const mockTranslations = (hass: MockHomeAssistant) => {
hass.mockWS( hass.mockWS(
"frontend/get_translations", "frontend/get_translations",
(/* msg: {language: string, category: string} */) => ({ resources: {} }) (/* msg: {language: string, category: string} */) => {
return { resources: {} };
}
); );
}; };

View File

@@ -15,10 +15,6 @@ class DemoCard extends PolymerElement {
margin: 0 0 20px; margin: 0 0 20px;
color: var(--primary-color); color: var(--primary-color);
} }
h2 small {
font-size: 0.5em;
color: var(--primary-text-color);
}
#card { #card {
max-width: 400px; max-width: 400px;
width: 100vw; width: 100vw;
@@ -38,12 +34,7 @@ class DemoCard extends PolymerElement {
} }
} }
</style> </style>
<h2> <h2>[[config.heading]]</h2>
[[config.heading]]
<template is="dom-if" if="[[_size]]">
<small>(size [[_size]])</small>
</template>
</h2>
<div class="root"> <div class="root">
<div id="card"></div> <div id="card"></div>
<template is="dom-if" if="[[showConfig]]"> <template is="dom-if" if="[[showConfig]]">
@@ -64,9 +55,6 @@ class DemoCard extends PolymerElement {
observer: "_configChanged", observer: "_configChanged",
}, },
showConfig: Boolean, showConfig: Boolean,
_size: {
type: Number,
},
}; };
} }
@@ -82,17 +70,6 @@ class DemoCard extends PolymerElement {
const el = this._createCardElement(safeLoad(config.config)[0]); const el = this._createCardElement(safeLoad(config.config)[0]);
card.appendChild(el); card.appendChild(el);
this._getSize(el);
}
async _getSize(el) {
await customElements.whenDefined(el.localName);
if (!("getCardSize" in el)) {
this._size = undefined;
return;
}
this._size = await el.getCardSize();
} }
_createCardElement(cardConfig) { _createCardElement(cardConfig) {

View File

@@ -93,8 +93,4 @@ class DemoAlarmPanelEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-alarm-panel-card", DemoAlarmPanelEntity);
interface HTMLElementTagNameMap {
"demo-hui-alarm-panel-card": DemoAlarmPanelEntity;
}
}

View File

@@ -75,8 +75,4 @@ class DemoConditional extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-conditional-card", DemoConditional);
interface HTMLElementTagNameMap {
"demo-hui-conditional-card": DemoConditional;
}
}

View File

@@ -239,8 +239,4 @@ class DemoEntities extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-entities-card", DemoEntities);
interface HTMLElementTagNameMap {
"demo-hui-entities-card": DemoEntities;
}
}

View File

@@ -91,8 +91,4 @@ class DemoButtonEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-entity-button-card", DemoButtonEntity);
interface HTMLElementTagNameMap {
"demo-hui-entity-button-card": DemoButtonEntity;
}
}

View File

@@ -132,8 +132,4 @@ class DemoEntityFilter extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-entity-filter-card", DemoEntityFilter);
interface HTMLElementTagNameMap {
"demo-hui-entity-filter-card": DemoEntityFilter;
}
}

View File

@@ -129,8 +129,4 @@ class DemoGaugeEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-gauge-card", DemoGaugeEntity);
interface HTMLElementTagNameMap {
"demo-hui-gauge-card": DemoGaugeEntity;
}
}

View File

@@ -232,8 +232,4 @@ class DemoGlanceEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-glance-card", DemoGlanceEntity);
interface HTMLElementTagNameMap {
"demo-hui-glance-card": DemoGlanceEntity;
}
}

View File

@@ -42,8 +42,4 @@ class DemoIframe extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-iframe-card", DemoIframe);
interface HTMLElementTagNameMap {
"demo-hui-iframe-card": DemoIframe;
}
}

View File

@@ -85,8 +85,4 @@ class DemoLightEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-light-card", DemoLightEntity);
interface HTMLElementTagNameMap {
"demo-hui-light-card": DemoLightEntity;
}
}

View File

@@ -183,8 +183,4 @@ class DemoMap extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-map-card", DemoMap);
interface HTMLElementTagNameMap {
"demo-hui-map-card": DemoMap;
}
}

View File

@@ -276,8 +276,4 @@ class DemoMarkdown extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-markdown-card", DemoMarkdown);
interface HTMLElementTagNameMap {
"demo-hui-markdown-card": DemoMarkdown;
}
}

View File

@@ -180,8 +180,4 @@ class DemoHuiMediaControlCard extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-media-control-card", DemoHuiMediaControlCard);
interface HTMLElementTagNameMap {
"demo-hui-media-control-card": DemoHuiMediaControlCard;
}
}

View File

@@ -77,8 +77,4 @@ class DemoHuiMediaPlayerRow extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-media-player-row", DemoHuiMediaPlayerRow);
interface HTMLElementTagNameMap {
"demo-hui-media-player-row": DemoHuiMediaPlayerRow;
}
}

View File

@@ -147,8 +147,4 @@ class DemoPictureElements extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-picture-elements-card", DemoPictureElements);
interface HTMLElementTagNameMap {
"demo-hui-picture-elements-card": DemoPictureElements;
}
}

View File

@@ -102,8 +102,4 @@ class DemoPictureEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-picture-entity-card", DemoPictureEntity);
interface HTMLElementTagNameMap {
"demo-hui-picture-entity-card": DemoPictureEntity;
}
}

View File

@@ -143,8 +143,4 @@ class DemoPictureGlance extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-picture-glance-card", DemoPictureGlance);
interface HTMLElementTagNameMap {
"demo-hui-picture-glance-card": DemoPictureGlance;
}
}

View File

@@ -52,8 +52,4 @@ export class DemoPlantEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-plant-card", DemoPlantEntity);
interface HTMLElementTagNameMap {
"demo-hui-plant-card": DemoPlantEntity;
}
}

View File

@@ -48,8 +48,4 @@ class DemoShoppingListEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-shopping-list-card", DemoShoppingListEntity);
interface HTMLElementTagNameMap {
"demo-hui-shopping-list-card": DemoShoppingListEntity;
}
}

View File

@@ -49,110 +49,6 @@ const ENTITIES = [
]; ];
const CONFIGS = [ const CONFIGS = [
{
heading: "Default Grid",
config: `
- type: grid
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
- type: entity
entity: device_tracker.demo_anne_therese
`,
},
{
heading: "Non-square Grid with 2 columns",
config: `
- type: grid
columns: 2
square: false
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
`,
},
{
heading: "Default Grid with title",
config: `
- type: grid
title: Kitchen
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
- type: entity
entity: device_tracker.demo_anne_therese
`,
},
{
heading: "Columns 4",
config: `
- type: grid
columns: 4
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
`,
},
{
heading: "Columns 2",
config: `
- type: grid
columns: 2
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
`,
},
{
heading: "Columns 1",
config: `
- type: grid
columns: 1
cards:
- type: entity
entity: light.kitchen_lights
`,
},
{
heading: "Size for single card",
config: `
- type: grid
cards:
- type: entity
entity: light.kitchen_lights
`,
},
{ {
heading: "Vertical Stack", heading: "Vertical Stack",
config: ` config: `
@@ -203,9 +99,45 @@ const CONFIGS = [
entity: light.bed_light entity: light.bed_light
`, `,
}, },
{
heading: "Default Grid",
config: `
- type: grid
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
- type: entity
entity: device_tracker.demo_anne_therese
`,
},
{
heading: "Non-square Grid with 2 columns",
config: `
- type: grid
columns: 2
square: false
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
`,
},
]; ];
@customElement("demo-hui-grid-and-stack-card") @customElement("demo-hui-stack-card")
class DemoStack extends LitElement { class DemoStack extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement; @query("#demos") private _demoRoot!: HTMLElement;
@@ -223,8 +155,4 @@ class DemoStack extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-stack-card", DemoStack);
interface HTMLElementTagNameMap {
"demo-hui-grid-and-stack-card": DemoStack;
}
}

View File

@@ -96,8 +96,4 @@ class DemoThermostatEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-thermostat-card", DemoThermostatEntity);
interface HTMLElementTagNameMap {
"demo-hui-thermostat-card": DemoThermostatEntity;
}
}

View File

@@ -61,9 +61,6 @@ const nameAsDomainEntry = createConfigEntry("ESPHome");
const longNameEntry = createConfigEntry( const longNameEntry = createConfigEntry(
"Entry with a super long name that is going to the next line" "Entry with a super long name that is going to the next line"
); );
const longNonBreakingNameEntry = createConfigEntry(
"EntryWithASuperLongNameThatDoesNotBreak"
);
const configPanelEntry = createConfigEntry("Config Panel", { const configPanelEntry = createConfigEntry("Config Panel", {
domain: "mqtt", domain: "mqtt",
localized_domain_name: "MQTT", localized_domain_name: "MQTT",
@@ -86,8 +83,7 @@ const setupRetryReasonEntry = createConfigEntry("Setup Retry", {
}); });
const setupRetryReasonMissingKeyEntry = createConfigEntry("Setup Retry", { const setupRetryReasonMissingKeyEntry = createConfigEntry("Setup Retry", {
state: "setup_retry", state: "setup_retry",
reason: reason: "resolve_error",
"HTTPSConnectionpool: Max retries exceeded with NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x9eedfc10>: Failed to establish a new connection: [Errno 113] Host is unreachable')",
}); });
const failedUnloadEntry = createConfigEntry("Failed Unload", { const failedUnloadEntry = createConfigEntry("Failed Unload", {
state: "failed_unload", state: "failed_unload",
@@ -145,7 +141,6 @@ const configEntries: Array<{
{ items: [optionsFlowEntry] }, { items: [optionsFlowEntry] },
{ items: [nameAsDomainEntry] }, { items: [nameAsDomainEntry] },
{ items: [longNameEntry] }, { items: [longNameEntry] },
{ items: [longNonBreakingNameEntry] },
{ items: [setupErrorEntry] }, { items: [setupErrorEntry] },
{ items: [migrationErrorEntry] }, { items: [migrationErrorEntry] },
{ items: [setupRetryEntry] }, { items: [setupRetryEntry] },
@@ -159,7 +154,6 @@ const configEntries: Array<{
setupErrorEntry, setupErrorEntry,
migrationErrorEntry, migrationErrorEntry,
longNameEntry, longNameEntry,
longNonBreakingNameEntry,
setupRetryEntry, setupRetryEntry,
failedUnloadEntry, failedUnloadEntry,
notLoadedEntry, notLoadedEntry,

View File

@@ -9,10 +9,13 @@ import {
} from "lit-element"; } from "lit-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { import {
LightColorModes, SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
SUPPORT_EFFECT, SUPPORT_EFFECT,
SUPPORT_FLASH, SUPPORT_FLASH,
SUPPORT_TRANSITION, SUPPORT_TRANSITION,
SUPPORT_WHITE_VALUE,
} from "../../../src/data/light"; } from "../../../src/data/light";
import "../../../src/dialogs/more-info/more-info-content"; import "../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
@@ -29,8 +32,7 @@ const ENTITIES = [
getEntity("light", "kitchen_light", "on", { getEntity("light", "kitchen_light", "on", {
friendly_name: "Brightness Light", friendly_name: "Brightness Light",
brightness: 200, brightness: 200,
supported_color_modes: [LightColorModes.BRIGHTNESS], supported_features: SUPPORT_BRIGHTNESS,
color_mode: LightColorModes.BRIGHTNESS,
}), }),
getEntity("light", "color_temperature_light", "on", { getEntity("light", "color_temperature_light", "on", {
friendly_name: "White Color Temperature Light", friendly_name: "White Color Temperature Light",
@@ -38,96 +40,20 @@ const ENTITIES = [
color_temp: 75, color_temp: 75,
min_mireds: 30, min_mireds: 30,
max_mireds: 150, max_mireds: 150,
supported_color_modes: [ supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP,
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
],
color_mode: LightColorModes.COLOR_TEMP,
}), }),
getEntity("light", "color_hs_light", "on", { getEntity("light", "color_effectslight", "on", {
friendly_name: "Color HS Light",
brightness: 255,
hs_color: [30, 100],
rgb_color: [30, 100, 255],
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
LightColorModes.HS,
],
color_mode: LightColorModes.HS,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_rgb_ct_light", "on", {
friendly_name: "Color RGB + CT Light",
brightness: 255,
color_temp: 75,
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
LightColorModes.RGB,
],
color_mode: LightColorModes.COLOR_TEMP,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_RGB_light", "on", {
friendly_name: "Color Effets Light", friendly_name: "Color Effets Light",
brightness: 255, brightness: 255,
rgb_color: [30, 100, 255], hs_color: [30, 100],
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION, white_value: 36,
supported_color_modes: [LightColorModes.BRIGHTNESS, LightColorModes.RGB], supported_features:
color_mode: LightColorModes.RGB, SUPPORT_BRIGHTNESS +
effect_list: ["random", "colorloop"], SUPPORT_EFFECT +
}), SUPPORT_FLASH +
getEntity("light", "color_rgbw_light", "on", { SUPPORT_COLOR +
friendly_name: "Color RGBW Light", SUPPORT_TRANSITION +
brightness: 255, SUPPORT_WHITE_VALUE,
rgbw_color: [30, 100, 255, 125],
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
LightColorModes.RGBW,
],
color_mode: LightColorModes.RGBW,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_rgbww_light", "on", {
friendly_name: "Color RGBWW Light",
brightness: 255,
rgbww_color: [30, 100, 255, 125, 10],
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
LightColorModes.RGBWW,
],
color_mode: LightColorModes.RGBWW,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_xy_light", "on", {
friendly_name: "Color XY Light",
brightness: 255,
xy_color: [30, 100],
rgb_color: [30, 100, 255],
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
LightColorModes.XY,
],
color_mode: LightColorModes.XY,
effect_list: ["random", "colorloop"], effect_list: ["random", "colorloop"],
}), }),
]; ];

View File

@@ -47,7 +47,9 @@ class HassioAddonRepositoryEl extends LitElement {
const repo = this.repo; const repo = this.repo;
let _addons = this.addons; let _addons = this.addons;
if (!this.hass.userData?.showAdvanced) { if (!this.hass.userData?.showAdvanced) {
_addons = _addons.filter((addon) => !addon.advanced); _addons = _addons.filter((addon) => {
return !addon.advanced;
});
} }
const addons = this._getAddons(_addons, this.filter); const addons = this._getAddons(_addons, this.filter);
@@ -66,7 +68,9 @@ class HassioAddonRepositoryEl extends LitElement {
} }
return html` return html`
<div class="content"> <div class="content">
<h1>${repo.name}</h1> <h1>
${repo.name}
</h1>
<div class="card-group"> <div class="card-group">
${addons.map( ${addons.map(
(addon) => html` (addon) => html`

View File

@@ -86,7 +86,9 @@ class HassioAddonStore extends LitElement {
main-page main-page
supervisor supervisor
> >
<span slot="header"> ${this.supervisor.localize("panel.store")} </span> <span slot="header">
${this.supervisor.localize("panel.store")}
</span>
<ha-button-menu <ha-button-menu
corner="BOTTOM_START" corner="BOTTOM_START"
slot="toolbar-icon" slot="toolbar-icon"
@@ -152,8 +154,8 @@ class HassioAddonStore extends LitElement {
repositories: HassioAddonRepository[], repositories: HassioAddonRepository[],
addons: HassioAddonInfo[], addons: HassioAddonInfo[],
filter?: string filter?: string
) => ) => {
repositories.sort(sortRepos).map((repo) => { return repositories.sort(sortRepos).map((repo) => {
const filteredAddons = addons.filter( const filteredAddons = addons.filter(
(addon) => addon.repository === repo.slug (addon) => addon.repository === repo.slug
); );
@@ -169,7 +171,8 @@ class HassioAddonStore extends LitElement {
></hassio-addon-repository> ></hassio-addon-repository>
` `
: html``; : html``;
}) });
}
); );
private _handleAction(ev: CustomEvent<ActionDetail>) { private _handleAction(ev: CustomEvent<ActionDetail>) {

View File

@@ -69,13 +69,13 @@ class HassioAddonAudio extends LitElement {
.selected=${this._selectedInput!} .selected=${this._selectedInput!}
> >
${this._inputDevices && ${this._inputDevices &&
this._inputDevices.map( this._inputDevices.map((item) => {
(item) => html` return html`
<paper-item device=${item.device || ""}> <paper-item device=${item.device || ""}>
${item.name} ${item.name}
</paper-item> </paper-item>
` `;
)} })}
</paper-listbox> </paper-listbox>
</paper-dropdown-menu> </paper-dropdown-menu>
<paper-dropdown-menu <paper-dropdown-menu
@@ -90,13 +90,13 @@ class HassioAddonAudio extends LitElement {
.selected=${this._selectedOutput!} .selected=${this._selectedOutput!}
> >
${this._outputDevices && ${this._outputDevices &&
this._outputDevices.map( this._outputDevices.map((item) => {
(item) => html` return html`
<paper-item device=${item.device || ""} <paper-item device=${item.device || ""}
>${item.name}</paper-item >${item.name}</paper-item
> >
` `;
)} })}
</paper-listbox> </paper-listbox>
</paper-dropdown-menu> </paper-dropdown-menu>
</div> </div>

View File

@@ -65,15 +65,19 @@ class HassioAddonConfig extends LitElement {
@query("ha-yaml-editor") private _editor?: HaYamlEditor; @query("ha-yaml-editor") private _editor?: HaYamlEditor;
public computeLabel = (entry: HaFormSchema): string => public computeLabel = (entry: HaFormSchema): string => {
return (
this.addon.translations[this.hass.language]?.configuration?.[entry.name] this.addon.translations[this.hass.language]?.configuration?.[entry.name]
?.name || ?.name ||
this.addon.translations.en?.configuration?.[entry.name].name || this.addon.translations.en?.configuration?.[entry.name].name ||
entry.name; entry.name
);
};
private _filteredShchema = memoizeOne( private _filteredShchema = memoizeOne(
(options: Record<string, unknown>, schema: HaFormSchema[]) => (options: Record<string, unknown>, schema: HaFormSchema[]) => {
schema.filter((entry) => entry.name in options || entry.required) return schema.filter((entry) => entry.name in options || entry.required);
}
); );
protected render(): TemplateResult { protected render(): TemplateResult {

View File

@@ -79,10 +79,12 @@ class HassioAddonNetwork extends LitElement {
"addon.configuration.network.host" "addon.configuration.network.host"
)} )}
</th> </th>
<th>${this.supervisor.localize("common.description")}</th> <th>
${this.supervisor.localize("common.description")}
</th>
</tr> </tr>
${this._config!.map( ${this._config!.map((item) => {
(item) => html` return html`
<tr> <tr>
<td>${item.container}</td> <td>${item.container}</td>
<td> <td>
@@ -98,8 +100,8 @@ class HassioAddonNetwork extends LitElement {
</td> </td>
<td>${this._computeDescription(item)}</td> <td>${this._computeDescription(item)}</td>
</tr> </tr>
` `;
)} })}
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -122,20 +124,25 @@ class HassioAddonNetwork extends LitElement {
} }
} }
private _computeDescription = (item: NetworkItem): string => private _computeDescription = (item: NetworkItem): string => {
return (
this.addon.translations[this.hass.language]?.network?.[item.container] this.addon.translations[this.hass.language]?.network?.[item.container]
?.description || ?.description ||
this.addon.translations.en?.network?.[item.container]?.description || this.addon.translations.en?.network?.[item.container]?.description ||
item.description; item.description
);
};
private _setNetworkConfig(): void { private _setNetworkConfig(): void {
const network = this.addon.network || {}; const network = this.addon.network || {};
const description = this.addon.network_description || {}; const description = this.addon.network_description || {};
const items: NetworkItem[] = Object.keys(network).map((key) => ({ const items: NetworkItem[] = Object.keys(network).map((key) => {
return {
container: key, container: key,
host: network[key], host: network[key],
description: description[key], description: description[key],
})); };
});
this._config = items.sort((a, b) => (a.container > b.container ? 1 : -1)); this._config = items.sort((a, b) => (a.container > b.container ? 1 : -1));
} }

View File

@@ -261,9 +261,13 @@ class HassioAddonInfo extends LitElement {
${this.supervisor.localize( ${this.supervisor.localize(
"addon.dashboard.visit_addon_page", "addon.dashboard.visit_addon_page",
"name", "name",
html`<a href="${this.addon.url!}" target="_blank" rel="noreferrer" html`<a
>${this.addon.name}</a href="${this.addon.url!}"
>` target="_blank"
rel="noreferrer"
>
${this.addon.name}
</a>`
)} )}
</div> </div>
<div class="addon-container"> <div class="addon-container">
@@ -562,7 +566,9 @@ class HassioAddonInfo extends LitElement {
<span slot="heading"> <span slot="heading">
${this.supervisor.localize("addon.dashboard.hostname")} ${this.supervisor.localize("addon.dashboard.hostname")}
</span> </span>
<code slot="description"> ${this.addon.hostname} </code> <code slot="description">
${this.addon.hostname}
</code>
</ha-settings-row> </ha-settings-row>
${metrics.map( ${metrics.map(
(metric) => (metric) =>
@@ -991,7 +997,7 @@ class HassioAddonInfo extends LitElement {
addons: [this.addon.slug], addons: [this.addon.slug],
homeassistant: false, homeassistant: false,
}, },
updateHandler: async () => this._updateAddon(), updateHandler: async () => await this._updateAddon(),
}); });
} }

View File

@@ -56,13 +56,13 @@ class HassioCardContent extends LitElement {
></ha-svg-icon> ></ha-svg-icon>
`} `}
<div> <div>
<div class="title">${this.title}</div> <div class="title">
${this.title}
</div>
<div class="addition"> <div class="addition">
${this.description} ${this.description}
${ ${/* treat as available when undefined */
/* treat as available when undefined */ this.available === false ? " (Not available)" : ""}
this.available === false ? " (Not available)" : ""
}
${this.datetime ${this.datetime
? html` ? html`
<ha-relative-time <ha-relative-time

View File

@@ -23,9 +23,13 @@ class SupervisorMetric extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
const roundedValue = roundWithOneDecimal(this.value); const roundedValue = roundWithOneDecimal(this.value);
return html`<ha-settings-row> return html`<ha-settings-row>
<span slot="heading"> ${this.description} </span> <span slot="heading">
${this.description}
</span>
<div slot="description" .title=${this.tooltip ?? ""}> <div slot="description" .title=${this.tooltip ?? ""}>
<span class="value"> ${roundedValue} % </span> <span class="value">
${roundedValue} %
</span>
<ha-bar <ha-bar
class="${classMap({ class="${classMap({
"target-warning": roundedValue > 50, "target-warning": roundedValue > 50,

View File

@@ -40,8 +40,9 @@ import { HomeAssistant } from "../../../src/types";
import { showDialogSupervisorUpdate } from "../dialogs/update/show-dialog-update"; import { showDialogSupervisorUpdate } from "../dialogs/update/show-dialog-update";
import { hassioStyle } from "../resources/hassio-style"; import { hassioStyle } from "../resources/hassio-style";
const computeVersion = (key: string, version: string): string => const computeVersion = (key: string, version: string): string => {
key === "os" ? version : `${key}-${version}`; return key === "os" ? version : `${key}-${version}`;
};
@customElement("hassio-update") @customElement("hassio-update")
export class HassioUpdate extends LitElement { export class HassioUpdate extends LitElement {
@@ -49,12 +50,11 @@ export class HassioUpdate extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor; @property({ attribute: false }) public supervisor!: Supervisor;
private _pendingUpdates = memoizeOne( private _pendingUpdates = memoizeOne((supervisor: Supervisor): number => {
(supervisor: Supervisor): number => return Object.keys(supervisor).filter(
Object.keys(supervisor).filter(
(value) => supervisor[value].update_available (value) => supervisor[value].update_available
).length ).length;
); });
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.supervisor) { if (!this.supervisor) {

View File

@@ -47,8 +47,7 @@ import { HassioNetworkDialogParams } from "./show-dialog-network";
const IP_VERSIONS = ["ipv4", "ipv6"]; const IP_VERSIONS = ["ipv4", "ipv6"];
@customElement("dialog-hassio-network") @customElement("dialog-hassio-network")
export class DialogHassioNetwork export class DialogHassioNetwork extends LitElement
extends LitElement
implements HassDialog<HassioNetworkDialogParams> { implements HassDialog<HassioNetworkDialogParams> {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@@ -77,9 +76,9 @@ export class DialogHassioNetwork
this._dirty = false; this._dirty = false;
this._curTabIndex = 0; this._curTabIndex = 0;
this.supervisor = params.supervisor; this.supervisor = params.supervisor;
this._interfaces = params.supervisor.network.interfaces.sort((a, b) => this._interfaces = params.supervisor.network.interfaces.sort((a, b) => {
a.primary > b.primary ? -1 : 1 return a.primary > b.primary ? -1 : 1;
); });
this._interface = { ...this._interfaces[this._curTabIndex] }; this._interface = { ...this._interfaces[this._curTabIndex] };
await this.updateComplete; await this.updateComplete;

View File

@@ -108,8 +108,8 @@ class HassioRegistriesDialog extends LitElement {
</mwc-button> </mwc-button>
` `
: html`${this._registries?.length : html`${this._registries?.length
? this._registries.map( ? this._registries.map((entry) => {
(entry) => html` return html`
<mwc-list-item class="option" hasMeta twoline> <mwc-list-item class="option" hasMeta twoline>
<span>${entry.registry}</span> <span>${entry.registry}</span>
<span slot="secondary" <span slot="secondary"
@@ -129,8 +129,8 @@ class HassioRegistriesDialog extends LitElement {
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon> <ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
</mwc-icon-button> </mwc-icon-button>
</mwc-list-item> </mwc-list-item>
` `;
) })
: html` : html`
<mwc-list-item> <mwc-list-item>
<span <span

View File

@@ -43,7 +43,7 @@ class HassioRepositoriesDialog extends LitElement {
@internalProperty() private _opened = false; @internalProperty() private _opened = false;
@internalProperty() private _processing = false; @internalProperty() private _prosessing = false;
@internalProperty() private _error?: string; @internalProperty() private _error?: string;
@@ -87,8 +87,8 @@ class HassioRepositoriesDialog extends LitElement {
${this._error ? html`<div class="error">${this._error}</div>` : ""} ${this._error ? html`<div class="error">${this._error}</div>` : ""}
<div class="form"> <div class="form">
${repositories.length ${repositories.length
? repositories.map( ? repositories.map((repo) => {
(repo) => html` return html`
<paper-item class="option"> <paper-item class="option">
<paper-item-body three-line> <paper-item-body three-line>
<div>${repo.name}</div> <div>${repo.name}</div>
@@ -105,9 +105,13 @@ class HassioRepositoriesDialog extends LitElement {
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon> <ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
</mwc-icon-button> </mwc-icon-button>
</paper-item> </paper-item>
` `;
) })
: html` <paper-item> No repositories </paper-item> `} : html`
<paper-item>
No repositories
</paper-item>
`}
<div class="layout horizontal bottom"> <div class="layout horizontal bottom">
<paper-input <paper-input
class="flex-auto" class="flex-auto"
@@ -119,11 +123,8 @@ class HassioRepositoriesDialog extends LitElement {
@keydown=${this._handleKeyAdd} @keydown=${this._handleKeyAdd}
></paper-input> ></paper-input>
<mwc-button @click=${this._addRepository}> <mwc-button @click=${this._addRepository}>
${this._processing ${this._prosessing
? html`<ha-circular-progress ? html`<ha-circular-progress active></ha-circular-progress>`
active
size="small"
></ha-circular-progress>`
: this._dialogParams!.supervisor.localize( : this._dialogParams!.supervisor.localize(
"dialog.repositories.add" "dialog.repositories.add"
)} )}
@@ -204,9 +205,11 @@ class HassioRepositoriesDialog extends LitElement {
if (!input || !input.value) { if (!input || !input.value) {
return; return;
} }
this._processing = true; this._prosessing = true;
const repositories = this._filteredRepositories(this._repositories!); const repositories = this._filteredRepositories(this._repositories!);
const newRepositories = repositories.map((repo) => repo.source); const newRepositories = repositories.map((repo) => {
return repo.source;
});
newRepositories.push(input.value); newRepositories.push(input.value);
try { try {
@@ -219,19 +222,25 @@ class HassioRepositoriesDialog extends LitElement {
} catch (err) { } catch (err) {
this._error = extractApiErrorMessage(err); this._error = extractApiErrorMessage(err);
} }
this._processing = false; this._prosessing = false;
} }
private async _removeRepository(ev: Event) { private async _removeRepository(ev: Event) {
const slug = (ev.currentTarget as any).slug; const slug = (ev.currentTarget as any).slug;
const repositories = this._filteredRepositories(this._repositories!); const repositories = this._filteredRepositories(this._repositories!);
const repository = repositories.find((repo) => repo.slug === slug); const repository = repositories.find((repo) => {
return repo.slug === slug;
});
if (!repository) { if (!repository) {
return; return;
} }
const newRepositories = repositories const newRepositories = repositories
.map((repo) => repo.source) .map((repo) => {
.filter((repo) => repo !== repository.source); return repo.source;
})
.filter((repo) => {
return repo !== repository.source;
});
try { try {
await setSupervisorOption(this.hass, { await setSupervisorOption(this.hass, {

View File

@@ -18,8 +18,7 @@ import "../../components/hassio-upload-snapshot";
import { HassioSnapshotUploadDialogParams } from "./show-dialog-snapshot-upload"; import { HassioSnapshotUploadDialogParams } from "./show-dialog-snapshot-upload";
@customElement("dialog-hassio-snapshot-upload") @customElement("dialog-hassio-snapshot-upload")
export class DialogHassioSnapshotUpload export class DialogHassioSnapshotUpload extends LitElement
extends LitElement
implements HassDialog<HassioSnapshotUploadDialogParams> { implements HassDialog<HassioSnapshotUploadDialogParams> {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@@ -58,7 +57,9 @@ export class DialogHassioSnapshotUpload
> >
<div slot="heading"> <div slot="heading">
<ha-header-bar> <ha-header-bar>
<span slot="title"> Upload snapshot </span> <span slot="title">
Upload snapshot
</span>
<mwc-icon-button slot="actionItems" dialogAction="cancel"> <mwc-icon-button slot="actionItems" dialogAction="cancel">
<ha-svg-icon .path=${mdiClose}></ha-svg-icon> <ha-svg-icon .path=${mdiClose}></ha-svg-icon>
</mwc-icon-button> </mwc-icon-button>

View File

@@ -53,13 +53,14 @@ const _computeFolders = (folders) => {
return list; return list;
}; };
const _computeAddons = (addons) => const _computeAddons = (addons) => {
addons.map((addon) => ({ return addons.map((addon) => ({
slug: addon.slug, slug: addon.slug,
name: addon.name, name: addon.name,
version: addon.version, version: addon.version,
checked: true, checked: true,
})); }));
};
interface AddonItem { interface AddonItem {
slug: string; slug: string;
@@ -121,7 +122,9 @@ class HassioSnapshotDialog extends LitElement {
<ha-dialog open @closing=${this._closeDialog} .heading=${true}> <ha-dialog open @closing=${this._closeDialog} .heading=${true}>
<div slot="heading"> <div slot="heading">
<ha-header-bar> <ha-header-bar>
<span slot="title"> ${this._computeName} </span> <span slot="title">
${this._computeName}
</span>
<mwc-icon-button slot="actionItems" dialogAction="cancel"> <mwc-icon-button slot="actionItems" dialogAction="cancel">
<ha-svg-icon .path=${mdiClose}></ha-svg-icon> <ha-svg-icon .path=${mdiClose}></ha-svg-icon>
</mwc-icon-button> </mwc-icon-button>
@@ -149,8 +152,8 @@ class HassioSnapshotDialog extends LitElement {
? html` ? html`
<div>Folders:</div> <div>Folders:</div>
<paper-dialog-scrollable class="no-margin-top"> <paper-dialog-scrollable class="no-margin-top">
${this._folders.map( ${this._folders.map((item) => {
(item) => html` return html`
<paper-checkbox <paper-checkbox
.checked=${item.checked} .checked=${item.checked}
@change="${(ev: Event) => @change="${(ev: Event) =>
@@ -161,8 +164,8 @@ class HassioSnapshotDialog extends LitElement {
> >
${item.name} ${item.name}
</paper-checkbox> </paper-checkbox>
` `;
)} })}
</paper-dialog-scrollable> </paper-dialog-scrollable>
` `
: ""} : ""}
@@ -170,8 +173,8 @@ class HassioSnapshotDialog extends LitElement {
? html` ? html`
<div>Add-on:</div> <div>Add-on:</div>
<paper-dialog-scrollable class="no-margin-top"> <paper-dialog-scrollable class="no-margin-top">
${this._addons.map( ${this._addons.map((item) => {
(item) => html` return html`
<paper-checkbox <paper-checkbox
.checked=${item.checked} .checked=${item.checked}
@change="${(ev: Event) => @change="${(ev: Event) =>
@@ -182,8 +185,8 @@ class HassioSnapshotDialog extends LitElement {
> >
${item.name} ${item.name}
</paper-checkbox> </paper-checkbox>
` `;
)} })}
</paper-dialog-scrollable> </paper-dialog-scrollable>
` `
: ""} : ""}

View File

@@ -132,7 +132,9 @@ class HassioSnapshots extends LitElement {
</ha-button-menu> </ha-button-menu>
<div class="content"> <div class="content">
<h1>${this.supervisor.localize("snapshot.create_snapshot")}</h1> <h1>
${this.supervisor.localize("snapshot.create_snapshot")}
</h1>
<p class="description"> <p class="description">
${this.supervisor.localize("snapshot.description")} ${this.supervisor.localize("snapshot.description")}
</p> </p>

View File

@@ -178,7 +178,7 @@ class HassioCoreInfo extends LitElement {
folders: ["homeassistant"], folders: ["homeassistant"],
homeassistant: true, homeassistant: true,
}, },
updateHandler: async () => this._updateCore(), updateHandler: async () => await this._updateCore(),
}); });
} }

View File

@@ -97,7 +97,9 @@ class HassioHostInfo extends LitElement {
<span slot="heading"> <span slot="heading">
${this.supervisor.localize("system.host.ip_address")} ${this.supervisor.localize("system.host.ip_address")}
</span> </span>
<span slot="description"> ${primaryIpAddress} </span> <span slot="description">
${primaryIpAddress}
</span>
<mwc-button <mwc-button
.label=${this.supervisor.localize("system.host.change")} .label=${this.supervisor.localize("system.host.change")}
@click=${this._changeNetworkClicked} @click=${this._changeNetworkClicked}

View File

@@ -264,7 +264,9 @@ class HassioSupervisorInfo extends LitElement {
title: this.supervisor.localize("system.supervisor.warning"), title: this.supervisor.localize("system.supervisor.warning"),
text: html`${this.supervisor.localize("system.supervisor.beta_warning")} text: html`${this.supervisor.localize("system.supervisor.beta_warning")}
<br /> <br />
<b> ${this.supervisor.localize("system.supervisor.beta_backup")} </b> <b>
${this.supervisor.localize("system.supervisor.beta_backup")}
</b>
<br /><br /> <br /><br />
${this.supervisor.localize("system.supervisor.beta_release_items")} ${this.supervisor.localize("system.supervisor.beta_release_items")}
<ul> <ul>

View File

@@ -87,13 +87,13 @@ class HassioSupervisorLog extends LitElement {
attr-for-selected="provider" attr-for-selected="provider"
.selected=${this._selectedLogProvider} .selected=${this._selectedLogProvider}
> >
${logProviders.map( ${logProviders.map((provider) => {
(provider) => html` return html`
<paper-item provider=${provider.key}> <paper-item provider=${provider.key}>
${provider.name} ${provider.name}
</paper-item> </paper-item>
` `;
)} })}
</paper-listbox> </paper-listbox>
</paper-dropdown-menu> </paper-dropdown-menu>
` `

View File

@@ -39,7 +39,9 @@ class HassioSystem extends LitElement {
main-page main-page
supervisor supervisor
> >
<span slot="header"> ${this.supervisor.localize("panel.system")} </span> <span slot="header">
${this.supervisor.localize("panel.system")}
</span>
<div class="content"> <div class="content">
<div class="card-group"> <div class="card-group">
<hassio-core-info <hassio-core-info

View File

@@ -14,10 +14,10 @@
"format:prettier": "prettier \"**/src/**/*.{js,ts,json,css,md}\" --write", "format:prettier": "prettier \"**/src/**/*.{js,ts,json,css,md}\" --write",
"lint:types": "tsc", "lint:types": "tsc",
"lint:lit": "lit-analyzer \"**/src/**/*.ts\" --format markdown --outFile result.md", "lint:lit": "lit-analyzer \"**/src/**/*.ts\" --format markdown --outFile result.md",
"lint": "yarn run lint:eslint && yarn run lint:prettier && yarn run lint:types", "lint": "npm run lint:eslint && npm run lint:prettier && npm run lint:types",
"format": "yarn run format:eslint && yarn run format:prettier", "format": "npm run format:eslint && npm run format:prettier",
"mocha": "node_modules/.bin/ts-mocha -p test-mocha/tsconfig.test.json --opts test-mocha/mocha.opts", "mocha": "node_modules/.bin/ts-mocha -p test-mocha/tsconfig.test.json --opts test-mocha/mocha.opts",
"test": "yarn run lint && yarn run mocha" "test": "npm run lint && npm run mocha"
}, },
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)", "author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
"license": "Apache-2.0", "license": "Apache-2.0",
@@ -115,8 +115,8 @@
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
"leaflet": "^1.4.0", "leaflet": "^1.4.0",
"leaflet-draw": "^1.0.4", "leaflet-draw": "^1.0.4",
"lit-element": "^2.5.0", "lit-element": "^2.4.0",
"lit-html": "^1.4.0", "lit-html": "^1.3.0",
"lit-virtualizer": "^0.4.2", "lit-virtualizer": "^0.4.2",
"marked": "2.0.0", "marked": "2.0.0",
"mdn-polyfills": "^5.16.0", "mdn-polyfills": "^5.16.0",
@@ -138,26 +138,24 @@
"vue": "^2.6.11", "vue": "^2.6.11",
"vue2-daterange-picker": "^0.5.1", "vue2-daterange-picker": "^0.5.1",
"web-animations-js": "^2.3.2", "web-animations-js": "^2.3.2",
"workbox-cacheable-response": "^6.1.5", "workbox-core": "^5.1.3",
"workbox-core": "^6.1.5", "workbox-precaching": "^5.1.3",
"workbox-expiration": "^6.1.5", "workbox-routing": "^5.1.3",
"workbox-precaching": "^6.1.5", "workbox-strategies": "^5.1.3",
"workbox-routing": "^6.1.5",
"workbox-strategies": "^6.1.5",
"xss": "^1.0.6" "xss": "^1.0.6"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.14.0", "@babel/core": "^7.11.6",
"@babel/plugin-external-helpers": "^7.12.13", "@babel/plugin-external-helpers": "^7.10.4",
"@babel/plugin-proposal-class-properties": "^7.13.0", "@babel/plugin-proposal-class-properties": "^7.10.4",
"@babel/plugin-proposal-decorators": "^7.13.15", "@babel/plugin-proposal-decorators": "^7.10.5",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4",
"@babel/plugin-proposal-object-rest-spread": "^7.13.8", "@babel/plugin-proposal-object-rest-spread": "^7.11.0",
"@babel/plugin-proposal-optional-chaining": "^7.13.12", "@babel/plugin-proposal-optional-chaining": "^7.11.0",
"@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-import-meta": "^7.10.4",
"@babel/preset-env": "^7.14.0", "@babel/preset-env": "^7.11.5",
"@babel/preset-typescript": "^7.13.0", "@babel/preset-typescript": "^7.10.4",
"@koa/cors": "^3.1.0", "@koa/cors": "^3.1.0",
"@open-wc/dev-server-hmr": "^0.0.2", "@open-wc/dev-server-hmr": "^0.0.2",
"@rollup/plugin-babel": "^5.2.1", "@rollup/plugin-babel": "^5.2.1",
@@ -168,6 +166,7 @@
"@types/chai": "^4.1.7", "@types/chai": "^4.1.7",
"@types/chromecast-caf-receiver": "^5.0.11", "@types/chromecast-caf-receiver": "^5.0.11",
"@types/chromecast-caf-sender": "^1.0.3", "@types/chromecast-caf-sender": "^1.0.3",
"@types/codemirror": "^0.0.97",
"@types/js-yaml": "^3.12.1", "@types/js-yaml": "^3.12.1",
"@types/leaflet": "^1.4.3", "@types/leaflet": "^1.4.3",
"@types/leaflet-draw": "^1.0.1", "@types/leaflet-draw": "^1.0.1",
@@ -177,23 +176,23 @@
"@types/resize-observer-browser": "^0.1.3", "@types/resize-observer-browser": "^0.1.3",
"@types/sortablejs": "^1.10.6", "@types/sortablejs": "^1.10.6",
"@types/webspeechapi": "^0.0.29", "@types/webspeechapi": "^0.0.29",
"@typescript-eslint/eslint-plugin": "^4.22.0", "@typescript-eslint/eslint-plugin": "^4.4.0",
"@typescript-eslint/parser": "^4.22.0", "@typescript-eslint/parser": "^4.4.0",
"@web/dev-server": "^0.0.24", "@web/dev-server": "^0.0.24",
"@web/dev-server-rollup": "^0.2.11", "@web/dev-server-rollup": "^0.2.11",
"babel-loader": "^8.1.0", "babel-loader": "^8.1.0",
"chai": "^4.2.0", "chai": "^4.2.0",
"cpx": "^1.5.0", "cpx": "^1.5.0",
"del": "^4.0.0", "del": "^4.0.0",
"eslint": "^7.25.0", "eslint": "^6.8.0",
"eslint-config-airbnb-typescript": "^12.3.1", "eslint-config-airbnb-typescript": "^7.2.1",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^6.10.1",
"eslint-import-resolver-webpack": "^0.13.0", "eslint-import-resolver-webpack": "^0.13.0",
"eslint-plugin-disable": "^2.0.1", "eslint-plugin-disable": "^2.0.1",
"eslint-plugin-import": "^2.22.1", "eslint-plugin-import": "^2.20.2",
"eslint-plugin-lit": "^1.3.0", "eslint-plugin-lit": "^1.2.0",
"eslint-plugin-prettier": "^3.4.0", "eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-wc": "^1.3.0", "eslint-plugin-wc": "^1.2.0",
"fancy-log": "^1.3.3", "fancy-log": "^1.3.3",
"fs-extra": "^7.0.1", "fs-extra": "^7.0.1",
"gulp": "^4.0.0", "gulp": "^4.0.0",
@@ -234,15 +233,15 @@
"webpack-cli": "^4.5.0", "webpack-cli": "^4.5.0",
"webpack-dev-server": "^3.11.2", "webpack-dev-server": "^3.11.2",
"webpack-manifest-plugin": "^3.0.0", "webpack-manifest-plugin": "^3.0.0",
"workbox-build": "^6.1.5" "workbox-build": "^5.1.3"
}, },
"_comment": "Polymer fixed to 3.1 because 3.2 throws on logbook page", "_comment": "Polymer fixed to 3.1 because 3.2 throws on logbook page",
"_comment_2": "Fix in https://github.com/Polymer/polymer/pull/5569", "_comment_2": "Fix in https://github.com/Polymer/polymer/pull/5569",
"resolutions": { "resolutions": {
"@webcomponents/webcomponentsjs": "^2.2.10", "@webcomponents/webcomponentsjs": "^2.2.10",
"@polymer/polymer": "3.1.0", "@polymer/polymer": "3.1.0",
"lit-html": "1.4.0", "lit-html": "1.3.0",
"lit-element": "2.5.0" "lit-element": "2.4.0"
}, },
"main": "src/home-assistant.js", "main": "src/home-assistant.js",
"husky": { "husky": {

View File

@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup( setup(
name="home-assistant-frontend", name="home-assistant-frontend",
version="20210503.0", version="20210423.0",
description="The Home Assistant frontend", description="The Home Assistant frontend",
url="https://github.com/home-assistant/home-assistant-polymer", url="https://github.com/home-assistant/home-assistant-polymer",
author="The Home Assistant Authors", author="The Home Assistant Authors",

View File

@@ -38,7 +38,11 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
@internalProperty() private _errorMessage?: string; @internalProperty() private _errorMessage?: string;
protected render() { protected render() {
return html` <form>${this._renderForm()}</form> `; return html`
<form>
${this._renderForm()}
</form>
`;
} }
protected firstUpdated(changedProps: PropertyValues) { protected firstUpdated(changedProps: PropertyValues) {

View File

@@ -17,8 +17,9 @@ export const hex2rgb = (hex: string): [number, number, number] => {
]; ];
}; };
export const rgb2hex = (rgb: [number, number, number]): string => export const rgb2hex = (rgb: [number, number, number]): string => {
`#${rgb_hex(rgb[0])}${rgb_hex(rgb[1])}${rgb_hex(rgb[2])}`; return `#${rgb_hex(rgb[0])}${rgb_hex(rgb[1])}${rgb_hex(rgb[2])}`;
};
// Conversion between LAB, XYZ and RGB from https://github.com/gka/chroma.js // Conversion between LAB, XYZ and RGB from https://github.com/gka/chroma.js
// Copyright (c) 2011-2019, Gregor Aisch // Copyright (c) 2011-2019, Gregor Aisch
@@ -48,10 +49,13 @@ const xyz_lab = (t: number) => {
return t / t2 + t0; return t / t2 + t0;
}; };
const xyz_rgb = (r: number) => const xyz_rgb = (r: number) => {
255 * (r <= 0.00304 ? 12.92 * r : 1.055 * r ** (1 / 2.4) - 0.055); return 255 * (r <= 0.00304 ? 12.92 * r : 1.055 * r ** (1 / 2.4) - 0.055);
};
const lab_xyz = (t: number) => (t > t1 ? t * t * t : t2 * (t - t0)); const lab_xyz = (t: number) => {
return t > t1 ? t * t * t : t2 * (t - t0);
};
// Conversions between RGB and LAB // Conversions between RGB and LAB
@@ -98,17 +102,3 @@ export const lab2hex = (lab: [number, number, number]): string => {
const rgb = lab2rgb(lab); const rgb = lab2rgb(lab);
return rgb2hex(rgb); return rgb2hex(rgb);
}; };
export const rgb2hsv = (
rgb: [number, number, number]
): [number, number, number] => {
const [r, g, b] = rgb;
const v = Math.max(r, g, b);
const c = v - Math.min(r, g, b);
const h =
c && (v === r ? (g - b) / c : v === g ? 2 + (b - r) / c : 4 + (r - g) / c);
return [60 * (h < 0 ? h + 6 : h), v && c / v, v];
};
export const rgb2hs = (rgb: [number, number, number]): [number, number] =>
rgb2hsv(rgb).slice(0, 2) as [number, number];

View File

@@ -4,9 +4,13 @@
export const labDarken = ( export const labDarken = (
lab: [number, number, number], lab: [number, number, number],
amount = 1 amount = 1
): [number, number, number] => [lab[0] - 18 * amount, lab[1], lab[2]]; ): [number, number, number] => {
return [lab[0] - 18 * amount, lab[1], lab[2]];
};
export const labBrighten = ( export const labBrighten = (
lab: [number, number, number], lab: [number, number, number],
amount = 1 amount = 1
): [number, number, number] => labDarken(lab, -amount); ): [number, number, number] => {
return labDarken(lab, -amount);
};

View File

@@ -2,9 +2,12 @@ import { PageNavigation } from "../../layouts/hass-tabs-subpage";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { isComponentLoaded } from "./is_component_loaded"; import { isComponentLoaded } from "./is_component_loaded";
export const canShowPage = (hass: HomeAssistant, page: PageNavigation) => export const canShowPage = (hass: HomeAssistant, page: PageNavigation) => {
return (
(isCore(page) || isLoadedIntegration(hass, page)) && (isCore(page) || isLoadedIntegration(hass, page)) &&
!hideAdvancedPage(hass, page); !hideAdvancedPage(hass, page)
);
};
const isLoadedIntegration = (hass: HomeAssistant, page: PageNavigation) => const isLoadedIntegration = (hass: HomeAssistant, page: PageNavigation) =>
!page.component || isComponentLoaded(hass, page.component); !page.component || isComponentLoaded(hass, page.component);

View File

@@ -86,10 +86,13 @@ export const LocalStorage = (
storageKey?: string, storageKey?: string,
property?: boolean, property?: boolean,
propertyOptions?: PropertyDeclaration propertyOptions?: PropertyDeclaration
): any => (clsElement: ClassElement) => { ): any => {
return (clsElement: ClassElement) => {
const key = String(clsElement.key); const key = String(clsElement.key);
storageKey = storageKey || String(clsElement.key); storageKey = storageKey || String(clsElement.key);
const initVal = clsElement.initializer ? clsElement.initializer() : undefined; const initVal = clsElement.initializer
? clsElement.initializer()
: undefined;
storage.addFromStorage(storageKey); storage.addFromStorage(storageKey);
@@ -98,8 +101,11 @@ export const LocalStorage = (
el.requestUpdate(clsElement.key, oldValue); el.requestUpdate(clsElement.key, oldValue);
}); });
const getValue = (): any => const getValue = (): any => {
storage.hasKey(storageKey!) ? storage.getValue(storageKey!) : initVal; return storage.hasKey(storageKey!)
? storage.getValue(storageKey!)
: initVal;
};
const setValue = (el: UpdatingElement, value: any) => { const setValue = (el: UpdatingElement, value: any) => {
let oldValue: unknown | undefined; let oldValue: unknown | undefined;
@@ -146,3 +152,4 @@ export const LocalStorage = (
}, },
}; };
}; };
};

View File

@@ -1,9 +1,8 @@
import type { LitElement } from "lit-element"; import type { LitElement } from "lit-element";
import type { ClassElement } from "../../types"; import type { ClassElement } from "../../types";
export const restoreScroll = (selector: string): any => ( export const restoreScroll = (selector: string): any => {
element: ClassElement return (element: ClassElement) => ({
) => ({
kind: "method", kind: "method",
placement: "prototype", placement: "prototype",
key: element.key, key: element.key,
@@ -31,3 +30,4 @@ export const restoreScroll = (selector: string): any => (
}; };
}, },
}); });
};

View File

@@ -1,9 +1,13 @@
// Load a resource and get a promise when loading done. // Load a resource and get a promise when loading done.
// From: https://davidwalsh.name/javascript-loader // From: https://davidwalsh.name/javascript-loader
const _load = (tag: "link" | "script" | "img", url: string, type?: "module") => const _load = (
tag: "link" | "script" | "img",
url: string,
type?: "module"
) => {
// This promise will be used by Promise.all to determine success or failure // This promise will be used by Promise.all to determine success or failure
new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const element = document.createElement(tag); const element = document.createElement(tag);
let attr = "src"; let attr = "src";
let parent = "body"; let parent = "body";
@@ -31,6 +35,8 @@ const _load = (tag: "link" | "script" | "img", url: string, type?: "module") =>
element[attr] = url; element[attr] = url;
document[parent].appendChild(element); document[parent].appendChild(element);
}); });
};
export const loadCSS = (url: string) => _load("link", url); export const loadCSS = (url: string) => _load("link", url);
export const loadJS = (url: string) => _load("script", url); export const loadJS = (url: string) => _load("script", url);
export const loadImg = (url: string) => _load("img", url); export const loadImg = (url: string) => _load("img", url);

View File

@@ -48,8 +48,8 @@ export const replaceTileLayer = (
const createTileLayer = ( const createTileLayer = (
leaflet: LeafletModuleType, leaflet: LeafletModuleType,
darkMode: boolean darkMode: boolean
): TileLayer => ): TileLayer => {
leaflet.tileLayer( return leaflet.tileLayer(
`https://{s}.basemaps.cartocdn.com/${ `https://{s}.basemaps.cartocdn.com/${
darkMode ? "dark_all" : "light_all" darkMode ? "dark_all" : "light_all"
}/{z}/{x}/{y}${leaflet.Browser.retina ? "@2x.png" : ".png"}`, }/{z}/{x}/{y}${leaflet.Browser.retina ? "@2x.png" : ".png"}`,
@@ -61,3 +61,4 @@ const createTileLayer = (
maxZoom: 20, maxZoom: 20,
} }
); );
};

View File

@@ -1,2 +1,3 @@
export const computeDomain = (entityId: string): string => export const computeDomain = (entityId: string): string => {
entityId.substr(0, entityId.indexOf(".")); return entityId.substr(0, entityId.indexOf("."));
};

View File

@@ -1,3 +1,4 @@
/** Compute the object ID of a state. */ /** Compute the object ID of a state. */
export const computeObjectId = (entityId: string): string => export const computeObjectId = (entityId: string): string => {
entityId.substr(entityId.indexOf(".") + 1); return entityId.substr(entityId.indexOf(".") + 1);
};

View File

@@ -1,5 +1,6 @@
import { HassEntity } from "home-assistant-js-websocket"; import { HassEntity } from "home-assistant-js-websocket";
import { computeDomain } from "./compute_domain"; import { computeDomain } from "./compute_domain";
export const computeStateDomain = (stateObj: HassEntity) => export const computeStateDomain = (stateObj: HassEntity) => {
computeDomain(stateObj.entity_id); return computeDomain(stateObj.entity_id);
};

View File

@@ -1,7 +1,8 @@
import { HassEntity } from "home-assistant-js-websocket"; import { HassEntity } from "home-assistant-js-websocket";
import { computeObjectId } from "./compute_object_id"; import { computeObjectId } from "./compute_object_id";
export const computeStateName = (stateObj: HassEntity): string => export const computeStateName = (stateObj: HassEntity): string => {
stateObj.attributes.friendly_name === undefined return stateObj.attributes.friendly_name === undefined
? computeObjectId(stateObj.entity_id).replace(/_/g, " ") ? computeObjectId(stateObj.entity_id).replace(/_/g, " ")
: stateObj.attributes.friendly_name || ""; : stateObj.attributes.friendly_name || "";
};

View File

@@ -1,4 +1,7 @@
import { HassEntity } from "home-assistant-js-websocket"; import { HassEntity } from "home-assistant-js-websocket";
export const hasLocation = (stateObj: HassEntity) => export const hasLocation = (stateObj: HassEntity) => {
"latitude" in stateObj.attributes && "longitude" in stateObj.attributes; return (
"latitude" in stateObj.attributes && "longitude" in stateObj.attributes
);
};

View File

@@ -3,6 +3,7 @@ import { HassEntity } from "home-assistant-js-websocket";
export const supportsFeature = ( export const supportsFeature = (
stateObj: HassEntity, stateObj: HassEntity,
feature: number feature: number
): boolean => ): boolean => {
// eslint-disable-next-line no-bitwise // eslint-disable-next-line no-bitwise
(stateObj.attributes.supported_features! & feature) !== 0; return (stateObj.attributes.supported_features! & feature) !== 0;
};

View File

@@ -119,7 +119,6 @@ export const extractColors = (url: string, downsampleColors = 16) =>
colorCount: downsampleColors, colorCount: downsampleColors,
}) })
.getPalette() .getPalette()
.then(({ foreground, background }) => ({ .then(({ foreground, background }) => {
background: background!, return { background: background!, foreground: foreground! };
foreground: foreground!, });
}));

View File

@@ -71,8 +71,8 @@ type FuzzyFilterSort = <T extends ScorableTextItem>(
items: T[] items: T[]
) => T[]; ) => T[];
export const fuzzyFilterSort: FuzzyFilterSort = (filter, items) => export const fuzzyFilterSort: FuzzyFilterSort = (filter, items) => {
items return items
.map((item) => { .map((item) => {
item.score = fuzzySequentialMatch(filter, item); item.score = fuzzySequentialMatch(filter, item);
return item; return item;
@@ -81,3 +81,4 @@ export const fuzzyFilterSort: FuzzyFilterSort = (filter, items) =>
.sort(({ score: scoreA = 0 }, { score: scoreB = 0 }) => .sort(({ score: scoreA = 0 }, { score: scoreB = 0 }) =>
scoreA > scoreB ? -1 : scoreA < scoreB ? 1 : 0 scoreA > scoreB ? -1 : scoreA < scoreB ? 1 : 0
); );
};

View File

@@ -2,7 +2,8 @@ export const afterNextRender = (cb: (value: unknown) => void): void => {
requestAnimationFrame(() => setTimeout(cb, 0)); requestAnimationFrame(() => setTimeout(cb, 0));
}; };
export const nextRender = () => export const nextRender = () => {
new Promise((resolve) => { return new Promise((resolve) => {
afterNextRender(resolve); afterNextRender(resolve);
}); });
};

View File

@@ -69,22 +69,11 @@ class HaCallServiceButton extends EventsMixin(PolymerElement) {
el.$.progress.actionSuccess(); el.$.progress.actionSuccess();
eventData.success = true; eventData.success = true;
}, },
function (err) { function () {
if ( el.progress = false;
el.domain === "homeassistant" &&
["restart", "stop"].includes(el.service) &&
err?.error?.message &&
err.error.message === "Connection lost"
) {
// We expect the service call to fail with 'Connection lost' when we restart or stop
el.$.progress.actionSuccess();
eventData.success = true;
} else {
el.$.progress.actionError(); el.$.progress.actionError();
eventData.success = false; eventData.success = false;
} }
el.progress = false;
}
) )
.then(function () { .then(function () {
el.fire("hass-service-called", eventData); el.fire("hass-service-called", eventData);

View File

@@ -485,7 +485,9 @@ export class HaDataTable extends LitElement {
data: DataTableRowData[], data: DataTableRowData[],
columns: SortableColumnContainer, columns: SortableColumnContainer,
filter: string filter: string
): Promise<DataTableRowData[]> => filterData(data, columns, filter) ): Promise<DataTableRowData[]> => {
return filterData(data, columns, filter);
}
); );
private _handleHeaderClick(ev: Event) { private _handleHeaderClick(ev: Event) {

View File

@@ -1,13 +1,13 @@
import { Remote, wrap } from "comlink"; import { wrap } from "comlink";
import type { Api } from "./sort_filter_worker"; import type { api } from "./sort_filter_worker";
type FilterDataType = Api["filterData"]; type FilterDataType = api["filterData"];
type FilterDataParamTypes = Parameters<FilterDataType>; type FilterDataParamTypes = Parameters<FilterDataType>;
type SortDataType = Api["sortData"]; type SortDataType = api["sortData"];
type SortDataParamTypes = Parameters<SortDataType>; type SortDataParamTypes = Parameters<SortDataType>;
let worker: Remote<Api> | undefined; let worker: any | undefined;
export const filterData = async ( export const filterData = async (
data: FilterDataParamTypes[0], data: FilterDataParamTypes[0],
@@ -18,7 +18,7 @@ export const filterData = async (
worker = wrap(new Worker(new URL("./sort_filter_worker", import.meta.url))); worker = wrap(new Worker(new URL("./sort_filter_worker", import.meta.url)));
} }
return worker.filterData(data, columns, filter); return await worker.filterData(data, columns, filter);
}; };
export const sortData = async ( export const sortData = async (
@@ -31,5 +31,5 @@ export const sortData = async (
worker = wrap(new Worker(new URL("./sort_filter_worker", import.meta.url))); worker = wrap(new Worker(new URL("./sort_filter_worker", import.meta.url)));
} }
return worker.sortData(data, columns, direction, sortColumn); return await worker.sortData(data, columns, direction, sortColumn);
}; };

View File

@@ -14,8 +14,8 @@ const filterData = (
filter: string filter: string
) => { ) => {
filter = filter.toUpperCase(); filter = filter.toUpperCase();
return data.filter((row) => return data.filter((row) => {
Object.entries(columns).some((columnEntry) => { return Object.entries(columns).some((columnEntry) => {
const [key, column] = columnEntry; const [key, column] = columnEntry;
if (column.filterable) { if (column.filterable) {
if ( if (
@@ -27,8 +27,8 @@ const filterData = (
} }
} }
return false; return false;
}) });
); });
}; };
const sortData = ( const sortData = (
@@ -80,6 +80,6 @@ const api = {
sortData, sortData,
}; };
export type Api = typeof api; export type api = typeof api;
expose(api); expose(api);

View File

@@ -64,8 +64,12 @@ export abstract class HaDeviceAutomationPicker<
private _createNoAutomation: (deviceId?: string) => T; private _createNoAutomation: (deviceId?: string) => T;
constructor( constructor(
localizeDeviceAutomation: HaDeviceAutomationPicker<T>["_localizeDeviceAutomation"], localizeDeviceAutomation: HaDeviceAutomationPicker<
fetchDeviceAutomations: HaDeviceAutomationPicker<T>["_fetchDeviceAutomations"], T
>["_localizeDeviceAutomation"],
fetchDeviceAutomations: HaDeviceAutomationPicker<
T
>["_fetchDeviceAutomations"],
createNoAutomation: HaDeviceAutomationPicker<T>["_createNoAutomation"] createNoAutomation: HaDeviceAutomationPicker<T>["_createNoAutomation"]
) { ) {
super(); super();

View File

@@ -8,7 +8,9 @@ import "../ha-paper-dropdown-menu";
import { HaDeviceAutomationPicker } from "./ha-device-automation-picker"; import { HaDeviceAutomationPicker } from "./ha-device-automation-picker";
@customElement("ha-device-condition-picker") @customElement("ha-device-condition-picker")
class HaDeviceConditionPicker extends HaDeviceAutomationPicker<DeviceCondition> { class HaDeviceConditionPicker extends HaDeviceAutomationPicker<
DeviceCondition
> {
protected get NO_AUTOMATION_TEXT() { protected get NO_AUTOMATION_TEXT() {
return this.hass.localize( return this.hass.localize(
"ui.panel.config.devices.automation.conditions.no_conditions" "ui.panel.config.devices.automation.conditions.no_conditions"

View File

@@ -212,7 +212,8 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
); );
} }
const outputDevices = inputDevices.map((device) => ({ const outputDevices = inputDevices.map((device) => {
return {
id: device.id, id: device.id,
name: computeDeviceName( name: computeDeviceName(
device, device,
@@ -222,7 +223,8 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
area: device.area_id area: device.area_id
? areaLookup[device.area_id].name ? areaLookup[device.area_id].name
: this.hass.localize("ui.components.device-picker.no_area"), : this.hass.localize("ui.components.device-picker.no_area"),
})); };
});
if (!outputDevices.length) { if (!outputDevices.length) {
return [ return [
{ {

View File

@@ -4,9 +4,9 @@ import { mixinBehaviors } from "@polymer/polymer/lib/legacy/class";
import type { Constructor } from "../../types"; import type { Constructor } from "../../types";
import { HaIronFocusablesHelper } from "./ha-iron-focusables-helper"; import { HaIronFocusablesHelper } from "./ha-iron-focusables-helper";
const paperDialogClass = customElements.get( const paperDialogClass = customElements.get("paper-dialog") as Constructor<
"paper-dialog" PaperDialogElement
) as Constructor<PaperDialogElement>; >;
// behavior that will override existing iron-overlay-behavior and call the fixed implementation // behavior that will override existing iron-overlay-behavior and call the fixed implementation
const haTabFixBehaviorImpl = { const haTabFixBehaviorImpl = {

View File

@@ -99,8 +99,12 @@ export class StateBadge extends LitElement {
hostStyle.backgroundImage = `url(${imageUrl})`; hostStyle.backgroundImage = `url(${imageUrl})`;
this._showIcon = false; this._showIcon = false;
} else if (stateObj.state === "on") { } else if (stateObj.state === "on") {
if (this.stateColor !== false && stateObj.attributes.rgb_color) { if (stateObj.attributes.hs_color && this.stateColor !== false) {
iconStyle.color = `rgb(${stateObj.attributes.rgb_color.join(",")})`; const hue = stateObj.attributes.hs_color[0];
const sat = stateObj.attributes.hs_color[1];
if (sat > 10) {
iconStyle.color = `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
}
} }
if (stateObj.attributes.brightness && this.stateColor !== false) { if (stateObj.attributes.brightness && this.stateColor !== false) {
const brightness = stateObj.attributes.brightness; const brightness = stateObj.attributes.brightness;

View File

@@ -6,6 +6,5 @@ export const analyticsLearnMore = (hass: HomeAssistant) => html`<a
.href=${documentationUrl(hass, "/integrations/analytics/")} .href=${documentationUrl(hass, "/integrations/analytics/")}
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
> >${hass.localize("ui.panel.config.core.section.core.analytics.learn_more")}</a
How we process your data >`;
</a>`;

View File

@@ -8,6 +8,7 @@ import {
property, property,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { isComponentLoaded } from "../common/config/is_component_loaded";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { Analytics, AnalyticsPreferences } from "../data/analytics"; import { Analytics, AnalyticsPreferences } from "../data/analytics";
import { haStyle } from "../resources/styles"; import { haStyle } from "../resources/styles";
@@ -16,18 +17,7 @@ import "./ha-checkbox";
import type { HaCheckbox } from "./ha-checkbox"; import type { HaCheckbox } from "./ha-checkbox";
import "./ha-settings-row"; import "./ha-settings-row";
const ADDITIONAL_PREFERENCES = [ const ADDITIONAL_PREFERENCES = ["usage", "statistics"];
{
key: "usage",
title: "Usage",
description: "Details of what you use with Home Assistant",
},
{
key: "statistics",
title: "Statistical data",
description: "Counts containing total number of datapoints",
},
];
declare global { declare global {
interface HASSDomEvents { interface HASSDomEvents {
@@ -57,9 +47,15 @@ export class HaAnalytics extends LitElement {
> >
</ha-checkbox> </ha-checkbox>
</span> </span>
<span slot="heading" data-for="base"> Basic analytics </span> <span slot="heading" data-for="base">
${this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.base.title`
)}
</span>
<span slot="description" data-for="base"> <span slot="description" data-for="base">
This includes information about your system. ${this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.base.description`
)}
</span> </span>
</ha-settings-row> </ha-settings-row>
${ADDITIONAL_PREFERENCES.map( ${ADDITIONAL_PREFERENCES.map(
@@ -68,23 +64,44 @@ export class HaAnalytics extends LitElement {
<span slot="prefix"> <span slot="prefix">
<ha-checkbox <ha-checkbox
@change=${this._handleRowCheckboxClick} @change=${this._handleRowCheckboxClick}
.checked=${this.analytics?.preferences[preference.key]} .checked=${this.analytics?.preferences[preference]}
.preference=${preference.key} .preference=${preference}
name=${preference.key} name=${preference}
> >
</ha-checkbox> </ha-checkbox>
${!baseEnabled ${!baseEnabled
? html`<paper-tooltip animation-delay="0" position="right"> ? html`<paper-tooltip animation-delay="0" position="right"
You need to enable basic analytics for this option to be >${this.hass.localize(
available "ui.panel.config.core.section.core.analytics.needs_base"
)}
</paper-tooltip>` </paper-tooltip>`
: ""} : ""}
</span> </span>
<span slot="heading" data-for=${preference.key}> <span slot="heading" data-for=${preference}>
${preference.title} ${preference === "usage"
? isComponentLoaded(this.hass, "hassio")
? this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.usage_supervisor.title`
)
: this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.usage.title`
)
: this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.${preference}.title`
)}
</span> </span>
<span slot="description" data-for=${preference.key}> <span slot="description" data-for=${preference}>
${preference.description} ${preference !== "usage"
? this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.${preference}.description`
)
: isComponentLoaded(this.hass, "hassio")
? this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.usage_supervisor.description`
)
: this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.usage.description`
)}
</span> </span>
</ha-settings-row>` </ha-settings-row>`
)} )}
@@ -99,9 +116,15 @@ export class HaAnalytics extends LitElement {
> >
</ha-checkbox> </ha-checkbox>
</span> </span>
<span slot="heading" data-for="diagnostics"> Diagnostics </span> <span slot="heading" data-for="diagnostics">
${this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.diagnostics.title`
)}
</span>
<span slot="description" data-for="diagnostics"> <span slot="description" data-for="diagnostics">
Share crash reports when unexpected errors occur. ${this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.diagnostics.description`
)}
</span> </span>
</ha-settings-row> </ha-settings-row>
`; `;
@@ -138,10 +161,7 @@ export class HaAnalytics extends LitElement {
preferences[preference] = checkbox.checked; preferences[preference] = checkbox.checked;
if ( if (ADDITIONAL_PREFERENCES.includes(preference) && checkbox.checked) {
ADDITIONAL_PREFERENCES.some((entry) => entry.key === preference) &&
checkbox.checked
) {
preferences.base = true; preferences.base = true;
} else if (preference === "base" && !checkbox.checked) { } else if (preference === "base" && !checkbox.checked) {
preferences.usage = false; preferences.usage = false;

View File

@@ -391,9 +391,11 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
`; `;
} }
private _area = memoizeOne((areaId: string): AreaRegistryEntry | undefined => private _area = memoizeOne((areaId: string):
this._areas?.find((area) => area.area_id === areaId) | AreaRegistryEntry
); | undefined => {
return this._areas?.find((area) => area.area_id === areaId);
});
private _clearValue(ev: Event) { private _clearValue(ev: Event) {
ev.stopPropagation(); ev.stopPropagation();

View File

@@ -36,8 +36,12 @@ class HaAttributes extends LitElement {
).map( ).map(
(attribute) => html` (attribute) => html`
<div class="data-entry"> <div class="data-entry">
<div class="key">${formatAttributeName(attribute)}</div> <div class="key">
<div class="value">${this.formatAttribute(attribute)}</div> ${formatAttributeName(attribute)}
</div>
<div class="value">
${this.formatAttribute(attribute)}
</div>
</div> </div>
` `
)} )}
@@ -88,9 +92,9 @@ class HaAttributes extends LitElement {
if (!this.stateObj) { if (!this.stateObj) {
return []; return [];
} }
return Object.keys(this.stateObj.attributes).filter( return Object.keys(this.stateObj.attributes).filter((key) => {
(key) => filtersArray.indexOf(key) === -1 return filtersArray.indexOf(key) === -1;
); });
} }
private formatAttribute(attribute: string): string | TemplateResult { private formatAttribute(attribute: string): string | TemplateResult {

View File

@@ -9,7 +9,6 @@ import {
property, property,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { styleMap } from "lit-html/directives/style-map";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import type { ToggleButton } from "../types"; import type { ToggleButton } from "../types";
import "./ha-svg-icon"; import "./ha-svg-icon";
@@ -20,8 +19,6 @@ export class HaButtonToggleGroup extends LitElement {
@property() public active?: string; @property() public active?: string;
@property({ type: Boolean }) public fullWidth = false;
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<div> <div>
@@ -36,11 +33,6 @@ export class HaButtonToggleGroup extends LitElement {
<ha-svg-icon .path=${button.iconPath}></ha-svg-icon> <ha-svg-icon .path=${button.iconPath}></ha-svg-icon>
</mwc-icon-button>` </mwc-icon-button>`
: html`<mwc-button : html`<mwc-button
style=${styleMap({
width: this.fullWidth
? `${100 / this.buttons.length}%`
: "initial",
})}
.value=${button.value} .value=${button.value}
?active=${this.active === button.value} ?active=${this.active === button.value}
@click=${this._handleClick} @click=${this._handleClick}

View File

@@ -124,6 +124,10 @@ export class HaCodeEditor extends UpdatingElement {
} }
</style>`; </style>`;
const container = document.createElement("span");
shadowRoot.appendChild(container);
this.codemirror = new this._loadedCodeMirror.EditorView({ this.codemirror = new this._loadedCodeMirror.EditorView({
state: this._loadedCodeMirror.EditorState.create({ state: this._loadedCodeMirror.EditorState.create({
doc: this._value, doc: this._value,
@@ -156,7 +160,7 @@ export class HaCodeEditor extends UpdatingElement {
], ],
}), }),
root: shadowRoot, root: shadowRoot,
parent: shadowRoot, parent: container,
}); });
} }

View File

@@ -2,7 +2,7 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */ /* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import { EventsMixin } from "../mixins/events-mixin"; import { EventsMixin } from "../mixins/events-mixin";
import { rgb2hs } from "../common/color/convert-color";
/** /**
* Color-picker custom element * Color-picker custom element
* *
@@ -114,12 +114,6 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
observer: "applyHsColor", observer: "applyHsColor",
}, },
// use these properties to update the state via attributes
desiredRgbColor: {
type: Object,
observer: "applyRgbColor",
},
// width, height and radius apply to the coordinates of // width, height and radius apply to the coordinates of
// of the canvas. // of the canvas.
// border width are relative to these numbers // border width are relative to these numbers
@@ -183,11 +177,8 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
this.drawMarker(); this.drawMarker();
if (this.desiredHsColor) { if (this.desiredHsColor) {
this.applyHsColor(this.desiredHsColor); this.setMarkerOnColor(this.desiredHsColor);
} this.applyColorToCanvas(this.desiredHsColor);
if (this.desiredRgbColor) {
this.applyRgbColor(this.desiredRgbColor);
} }
this.interactionLayer.addEventListener("mousedown", (ev) => this.interactionLayer.addEventListener("mousedown", (ev) =>
@@ -291,13 +282,12 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
processUserSelect(ev) { processUserSelect(ev) {
const canvasXY = this.convertToCanvasCoordinates(ev.clientX, ev.clientY); const canvasXY = this.convertToCanvasCoordinates(ev.clientX, ev.clientY);
const hs = this.getColor(canvasXY.x, canvasXY.y); const hs = this.getColor(canvasXY.x, canvasXY.y);
const rgb = this.getRgbColor(canvasXY.x, canvasXY.y); this.onColorSelect(hs);
this.onColorSelect(hs, rgb);
} }
// apply color to marker position and canvas // apply color to marker position and canvas
onColorSelect(hs, rgb) { onColorSelect(hs) {
this.setMarkerOnColor(hs); // marker always follows mouse 'raw' hs value (= mouse position) this.setMarkerOnColor(hs); // marker always follows mounse 'raw' hs value (= mouse position)
if (!this.ignoreSegments) { if (!this.ignoreSegments) {
// apply segments if needed // apply segments if needed
hs = this.applySegmentFilter(hs); hs = this.applySegmentFilter(hs);
@@ -311,11 +301,11 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
// eventually after throttle limit has passed // eventually after throttle limit has passed
clearTimeout(this.ensureFinalSelect); clearTimeout(this.ensureFinalSelect);
this.ensureFinalSelect = setTimeout(() => { this.ensureFinalSelect = setTimeout(() => {
this.fireColorSelected(hs, rgb); // do it for the final time this.fireColorSelected(hs); // do it for the final time
}, this.throttle); }, this.throttle);
return; return;
} }
this.fireColorSelected(hs, rgb); // do it this.fireColorSelected(hs); // do it
this.colorSelectIsThrottled = true; this.colorSelectIsThrottled = true;
setTimeout(() => { setTimeout(() => {
this.colorSelectIsThrottled = false; this.colorSelectIsThrottled = false;
@@ -323,9 +313,9 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
} }
// set color values and fire colorselected event // set color values and fire colorselected event
fireColorSelected(hs, rgb) { fireColorSelected(hs) {
this.hsColor = hs; this.hsColor = hs;
this.fire("colorselected", { hs, rgb }); this.fire("colorselected", { hs: { h: hs.h, s: hs.s } });
} }
/* /*
@@ -373,11 +363,6 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
this.applyColorToCanvas(hs); this.applyColorToCanvas(hs);
} }
applyRgbColor(rgb) {
const [h, s] = rgb2hs(rgb);
this.applyHsColor({ h, s });
}
/* /*
* input processing helpers * input processing helpers
*/ */
@@ -410,15 +395,6 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
return { h: hue, s: sat }; return { h: hue, s: sat };
} }
getRgbColor(x, y) {
// get current pixel
const imageData = this.backgroundLayer
.getContext("2d")
.getImageData(x + 250, y + 250, 1, 1);
const pixel = imageData.data;
return { r: pixel[0], g: pixel[1], b: pixel[2] };
}
applySegmentFilter(hs) { applySegmentFilter(hs) {
// apply hue segment steps // apply hue segment steps
if (this.hueSegments) { if (this.hueSegments) {
@@ -492,7 +468,7 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
.getPropertyValue("--wheel-bordercolor") .getPropertyValue("--wheel-bordercolor")
.trim(); .trim();
const wheelShadow = wheelStyle.getPropertyValue("--wheel-shadow").trim(); const wheelShadow = wheelStyle.getPropertyValue("--wheel-shadow").trim();
// extract shadow properties from CSS variable // extract shadow properties from CCS variable
// the shadow should be defined as: "10px 5px 5px 0px COLOR" // the shadow should be defined as: "10px 5px 5px 0px COLOR"
if (wheelShadow !== "none") { if (wheelShadow !== "none") {
const values = wheelShadow.split("px "); const values = wheelShadow.split("px ");

View File

@@ -45,13 +45,16 @@ const i18n = {
clear: "Clear", clear: "Clear",
today: "Today", today: "Today",
cancel: "Cancel", cancel: "Cancel",
formatTitle: (monthName, fullYear) => monthName + " " + fullYear, formatTitle: (monthName, fullYear) => {
formatDate: (d: { day: number; month: number; year: number }) => return monthName + " " + fullYear;
[ },
formatDate: (d: { day: number; month: number; year: number }) => {
return [
("0000" + String(d.year)).slice(-4), ("0000" + String(d.year)).slice(-4),
("0" + String(d.month + 1)).slice(-2), ("0" + String(d.month + 1)).slice(-2),
("0" + String(d.day)).slice(-2), ("0" + String(d.day)).slice(-2),
].join("-"), ].join("-");
},
parseDate: (text: string) => { parseDate: (text: string) => {
const parts = text.split("-"); const parts = text.split("-");
const today = new Date(); const today = new Date();

View File

@@ -88,7 +88,9 @@ export class HaDateRangePicker extends LitElement {
> >
<mwc-list @action=${this._setDateRange} activatable> <mwc-list @action=${this._setDateRange} activatable>
${Object.keys(this.ranges).map( ${Object.keys(this.ranges).map(
(name) => html`<mwc-list-item> ${name} </mwc-list-item>` (name) => html`<mwc-list-item>
${name}
</mwc-list-item>`
)} )}
</mwc-list> </mwc-list>
</div>` </div>`

View File

@@ -28,7 +28,9 @@ export class HaDialog extends MwcDialog {
} }
protected renderHeading() { protected renderHeading() {
return html`<slot name="heading"> ${super.renderHeading()} </slot>`; return html`<slot name="heading">
${super.renderHeading()}
</slot>`;
} }
protected static get styles(): CSSResult[] { protected static get styles(): CSSResult[] {

View File

@@ -79,7 +79,9 @@ export class HaFileUpload extends LitElement {
dragged: this._drag, dragged: this._drag,
})} })}
> >
<label for="input" slot="label"> ${this.label} </label> <label for="input" slot="label">
${this.label}
</label>
<iron-input slot="input"> <iron-input slot="input">
<input <input
id="input" id="input"

View File

@@ -80,8 +80,7 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
@selected-items-changed=${this._valueChanged} @selected-items-changed=${this._valueChanged}
@iron-select=${this._onSelect} @iron-select=${this._onSelect}
> >
${ ${// TS doesn't work with union array types https://github.com/microsoft/TypeScript/issues/36390
// TS doesn't work with union array types https://github.com/microsoft/TypeScript/issues/36390
// @ts-ignore // @ts-ignore
options.map((item: string | [string, string]) => { options.map((item: string | [string, string]) => {
const value = this._optionValue(item); const value = this._optionValue(item);
@@ -94,8 +93,7 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
${this._optionLabel(item)} ${this._optionLabel(item)}
</paper-icon-item> </paper-icon-item>
`; `;
}) })}
}
</paper-listbox> </paper-listbox>
</paper-menu-button> </paper-menu-button>
`; `;

View File

@@ -41,8 +41,7 @@ export class HaFormSelect extends LitElement implements HaFormElement {
.selected=${this.data} .selected=${this.data}
@selected-item-changed=${this._valueChanged} @selected-item-changed=${this._valueChanged}
> >
${ ${// TS doesn't work with union array types https://github.com/microsoft/TypeScript/issues/36390
// TS doesn't work with union array types https://github.com/microsoft/TypeScript/issues/36390
// @ts-ignore // @ts-ignore
this.schema.options!.map( this.schema.options!.map(
(item: string | [string, string]) => html` (item: string | [string, string]) => html`
@@ -50,8 +49,7 @@ export class HaFormSelect extends LitElement implements HaFormElement {
${this._optionLabel(item)} ${this._optionLabel(item)}
</paper-item> </paper-item>
` `
) )}
}
</paper-listbox> </paper-listbox>
</ha-paper-dropdown-menu> </ha-paper-dropdown-menu>
`; `;

View File

@@ -4,9 +4,9 @@ import { style } from "@material/mwc-formfield/mwc-formfield-css";
import { css, CSSResult, customElement } from "lit-element"; import { css, CSSResult, customElement } from "lit-element";
import { Constructor } from "../types"; import { Constructor } from "../types";
const MwcFormfield = customElements.get( const MwcFormfield = customElements.get("mwc-formfield") as Constructor<
"mwc-formfield" Formfield
) as Constructor<Formfield>; >;
@customElement("ha-formfield") @customElement("ha-formfield")
export class HaFormfield extends MwcFormfield { export class HaFormfield extends MwcFormfield {

View File

@@ -36,7 +36,9 @@ export class HaSelectSelector extends LitElement {
> >
${this.selector.select.options.map( ${this.selector.select.options.map(
(item: string) => html` (item: string) => html`
<paper-item .itemValue=${item}> ${item} </paper-item> <paper-item .itemValue=${item}>
${item}
</paper-item>
` `
)} )}
</paper-listbox> </paper-listbox>

View File

@@ -140,11 +140,13 @@ export class HaServiceControl extends LitElement {
const fields = Object.entries( const fields = Object.entries(
serviceDomains[domain][serviceName].fields serviceDomains[domain][serviceName].fields
).map(([key, value]) => ({ ).map(([key, value]) => {
return {
key, key,
...value, ...value,
selector: value.selector as Selector | undefined, selector: value.selector as Selector | undefined,
})); };
});
return { return {
...serviceDomains[domain][serviceName], ...serviceDomains[domain][serviceName],
fields, fields,

View File

@@ -5,9 +5,9 @@ import type { PaperTabsElement } from "@polymer/paper-tabs/paper-tabs";
import { customElement } from "lit-element"; import { customElement } from "lit-element";
import { Constructor } from "../types"; import { Constructor } from "../types";
const PaperTabs = customElements.get( const PaperTabs = customElements.get("paper-tabs") as Constructor<
"paper-tabs" PaperTabsElement
) as Constructor<PaperTabsElement>; >;
let subTemplate: HTMLTemplateElement; let subTemplate: HTMLTemplateElement;

View File

@@ -2,9 +2,9 @@ import "@polymer/paper-toast/paper-toast";
import type { PaperToastElement } from "@polymer/paper-toast/paper-toast"; import type { PaperToastElement } from "@polymer/paper-toast/paper-toast";
import type { Constructor } from "../types"; import type { Constructor } from "../types";
const PaperToast = customElements.get( const PaperToast = customElements.get("paper-toast") as Constructor<
"paper-toast" PaperToastElement
) as Constructor<PaperToastElement>; >;
export class HaToast extends PaperToast { export class HaToast extends PaperToast {
private _resizeListener?: (obj: { matches: boolean }) => unknown; private _resizeListener?: (obj: { matches: boolean }) => unknown;

View File

@@ -122,7 +122,9 @@ export class HaMediaPlayerBrowse extends LitElement {
}); });
} else { } else {
return html` return html`
<div class="container">${this._renderError(this._error)}</div> <div class="container">
${this._renderError(this._error)}
</div>
`; `;
} }
} }
@@ -200,7 +202,13 @@ export class HaMediaPlayerBrowse extends LitElement {
` `
: ""} : ""}
<h1 class="title">${currentItem.title}</h1> <h1 class="title">${currentItem.title}</h1>
${subtitle ? html` <h2 class="subtitle">${subtitle}</h2> ` : ""} ${subtitle
? html`
<h2 class="subtitle">
${subtitle}
</h2>
`
: ""}
</div> </div>
${currentItem.can_play && (!currentItem.thumbnail || !this._narrow) ${currentItem.can_play && (!currentItem.thumbnail || !this._narrow)
? html` ? html`
@@ -239,7 +247,9 @@ export class HaMediaPlayerBrowse extends LitElement {
<div class="content" @scroll=${this._scroll} @touchmove=${this._scroll}> <div class="content" @scroll=${this._scroll} @touchmove=${this._scroll}>
${this._error ${this._error
? html` ? html`
<div class="container">${this._renderError(this._error)}</div> <div class="container">
${this._renderError(this._error)}
</div>
` `
: currentItem.children?.length : currentItem.children?.length
? childrenMediaClass.layout === "grid" ? childrenMediaClass.layout === "grid"

View File

@@ -143,7 +143,7 @@ class HatScriptGraph extends LitElement {
const trace = this.trace.trace[path] as ChooseActionTraceStep[] | undefined; const trace = this.trace.trace[path] as ChooseActionTraceStep[] | undefined;
const trace_path = trace?.[0].result const trace_path = trace?.[0].result
? trace[0].result.choice === "default" ? trace[0].result.choice === "default"
? [Array.isArray(config.choose) ? config.choose.length : 0] ? [config.choose?.length || 0]
: [trace[0].result.choice] : [trace[0].result.choice]
: []; : [];
return html` return html`
@@ -167,8 +167,7 @@ class HatScriptGraph extends LitElement {
nofocus nofocus
></hat-graph-node> ></hat-graph-node>
${config.choose ${config.choose?.map((branch, i) => {
? ensureArray(config.choose)?.map((branch, i) => {
const branch_path = `${path}/choose/${i}`; const branch_path = `${path}/choose/${i}`;
const track_this = const track_this =
trace !== undefined && trace[0].result?.choice === i; trace !== undefined && trace[0].result?.choice === i;
@@ -192,8 +191,7 @@ class HatScriptGraph extends LitElement {
)} )}
</hat-graph> </hat-graph>
`; `;
}) })}
: ""}
<hat-graph> <hat-graph>
<hat-graph-spacer <hat-graph-spacer
class=${classMap({ class=${classMap({

View File

@@ -9,6 +9,7 @@ export interface AnalyticsPreferences {
export interface Analytics { export interface Analytics {
preferences: AnalyticsPreferences; preferences: AnalyticsPreferences;
onboarded: boolean;
} }
export const getAnalyticsDetails = (hass: HomeAssistant) => export const getAnalyticsDetails = (hass: HomeAssistant) =>

View File

@@ -1,7 +1,8 @@
import { Trigger, Condition } from "./automation"; import { Trigger, Condition } from "./automation";
export const describeTrigger = (trigger: Trigger) => export const describeTrigger = (trigger: Trigger) => {
`${trigger.platform} trigger`; return `${trigger.platform} trigger`;
};
export const describeCondition = (condition: Condition) => { export const describeCondition = (condition: Condition) => {
if (condition.alias) { if (condition.alias) {

Some files were not shown because too many files have changed in this diff Show More