diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 7010954..63be507 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -21,6 +21,7 @@ module.exports = {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-this-alias': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
+ '@typescript-eslint/no-unsafe-declaration-merging': 'off',
'no-console': 'error',
'prettier/prettier': [
'error',
diff --git a/src/data.ts b/src/data.ts
index ce78242..2d38067 100644
--- a/src/data.ts
+++ b/src/data.ts
@@ -3,76 +3,86 @@ import { Utils } from './utils';
export class MeteoalarmEventInfo {
constructor(
- public type: MeteoalarmEventType,
- public fullName: string,
- public icon: string
+ public type: MeteoalarmEventType,
+ public fullName: string,
+ public icon: string,
) {}
get translationKey(): string {
- return 'events.' + this.fullName.toLocaleLowerCase()
- .replace(' ', '_')
- .replace('/', '_')
- .replace('-', '_');
+ return (
+ 'events.' +
+ this.fullName.toLocaleLowerCase().replace(' ', '_').replace('/', '_').replace('-', '_')
+ );
}
}
export class MeteoalarmLevelInfo {
constructor(
- public type: MeteoalarmLevelType,
- public fullName: string,
- public color: string
+ public type: MeteoalarmLevelType,
+ public fullName: string,
+ public color: string,
) {}
get translationKey(): string {
- return 'messages.' + this.fullName.toLocaleLowerCase()
- .replace(' ', '_')
- .replace('/', '_')
- .replace('-', '_');
+ return (
+ 'messages.' +
+ this.fullName.toLocaleLowerCase().replace(' ', '_').replace('/', '_').replace('-', '_')
+ );
}
}
export class MeteoalarmData {
static get events(): MeteoalarmEventInfo[] {
// Use some new icons
- if(!Utils.minHAversion(2022, 8)) {
+ if (!Utils.minHAversion(2022, 8)) {
/* eslint-disable-next-line no-console */
- console.warn('MeteoalarmCard: You are using old HA version! Please update to at least 2022.08 for the best experience.');
+ console.warn(
+ 'MeteoalarmCard: You are using old HA version! Please update to at least 2022.08 for the best experience.',
+ );
}
const tsunami = Utils.minHAversion(2022, 6) ? 'tsunami' : 'waves';
const dust = Utils.minHAversion(2022, 8) ? 'weather-dust' : 'weather-windy';
// This list should be ordered from most to least dangerous
return [
- new MeteoalarmEventInfo(MeteoalarmEventType.Nuclear, 'Nuclear Event', 'radioactive'),
- new MeteoalarmEventInfo(MeteoalarmEventType.Hurricane, 'Hurricane', 'weather-hurricane'),
- new MeteoalarmEventInfo(MeteoalarmEventType.Tornado, 'Tornado', 'weather-tornado'),
- new MeteoalarmEventInfo(MeteoalarmEventType.CoastalEvent, 'Coastal Event', tsunami),
- new MeteoalarmEventInfo(MeteoalarmEventType.Tsunami, 'Tsunami', tsunami),
- new MeteoalarmEventInfo(MeteoalarmEventType.ForestFire, 'Forest Fire', 'pine-tree-fire'),
- new MeteoalarmEventInfo(MeteoalarmEventType.Avalanches, 'Avalanches', 'image-filter-hdr'),
- new MeteoalarmEventInfo(MeteoalarmEventType.Earthquake, 'Earthquake', 'image-broken-variant'),
- new MeteoalarmEventInfo(MeteoalarmEventType.Volcano, 'Volcanic Activity','volcano-outline'),
- new MeteoalarmEventInfo(MeteoalarmEventType.Flooding, 'Flooding', 'home-flood'),
- new MeteoalarmEventInfo(MeteoalarmEventType.SeaEvent, 'Sea Event', 'ferry'),
- new MeteoalarmEventInfo(MeteoalarmEventType.Thunderstorms, 'Thunderstorms', 'weather-lightning'),
- new MeteoalarmEventInfo(MeteoalarmEventType.Rain, 'Rain', 'weather-pouring'),
- new MeteoalarmEventInfo(MeteoalarmEventType.SnowIce, 'Snow/Ice', 'weather-snowy-heavy'),
- new MeteoalarmEventInfo(MeteoalarmEventType.HighTemperature, 'High Temperature', 'thermometer'),
- new MeteoalarmEventInfo(MeteoalarmEventType.LowTemperature, 'Low Temperature', 'snowflake'),
- new MeteoalarmEventInfo(MeteoalarmEventType.Dust, 'Dust', dust),
- new MeteoalarmEventInfo(MeteoalarmEventType.Wind, 'Wind', 'weather-windy'),
- new MeteoalarmEventInfo(MeteoalarmEventType.Fog, 'Fog', 'weather-fog'),
- new MeteoalarmEventInfo(MeteoalarmEventType.AirQuality, 'Air Quality', 'air-filter'),
- new MeteoalarmEventInfo(MeteoalarmEventType.Unknown, 'Unknown Event', 'alert-circle-outline')
+ new MeteoalarmEventInfo(MeteoalarmEventType.Nuclear, 'Nuclear Event', 'radioactive'),
+ new MeteoalarmEventInfo(MeteoalarmEventType.Hurricane, 'Hurricane', 'weather-hurricane'),
+ new MeteoalarmEventInfo(MeteoalarmEventType.Tornado, 'Tornado', 'weather-tornado'),
+ new MeteoalarmEventInfo(MeteoalarmEventType.CoastalEvent, 'Coastal Event', tsunami),
+ new MeteoalarmEventInfo(MeteoalarmEventType.Tsunami, 'Tsunami', tsunami),
+ new MeteoalarmEventInfo(MeteoalarmEventType.ForestFire, 'Forest Fire', 'pine-tree-fire'),
+ new MeteoalarmEventInfo(MeteoalarmEventType.Avalanches, 'Avalanches', 'image-filter-hdr'),
+ new MeteoalarmEventInfo(MeteoalarmEventType.Earthquake, 'Earthquake', 'image-broken-variant'),
+ new MeteoalarmEventInfo(MeteoalarmEventType.Volcano, 'Volcanic Activity', 'volcano-outline'),
+ new MeteoalarmEventInfo(MeteoalarmEventType.Flooding, 'Flooding', 'home-flood'),
+ new MeteoalarmEventInfo(MeteoalarmEventType.SeaEvent, 'Sea Event', 'ferry'),
+ new MeteoalarmEventInfo(
+ MeteoalarmEventType.Thunderstorms,
+ 'Thunderstorms',
+ 'weather-lightning',
+ ),
+ new MeteoalarmEventInfo(MeteoalarmEventType.Rain, 'Rain', 'weather-pouring'),
+ new MeteoalarmEventInfo(MeteoalarmEventType.SnowIce, 'Snow/Ice', 'weather-snowy-heavy'),
+ new MeteoalarmEventInfo(
+ MeteoalarmEventType.HighTemperature,
+ 'High Temperature',
+ 'thermometer',
+ ),
+ new MeteoalarmEventInfo(MeteoalarmEventType.LowTemperature, 'Low Temperature', 'snowflake'),
+ new MeteoalarmEventInfo(MeteoalarmEventType.Dust, 'Dust', dust),
+ new MeteoalarmEventInfo(MeteoalarmEventType.Wind, 'Wind', 'weather-windy'),
+ new MeteoalarmEventInfo(MeteoalarmEventType.Fog, 'Fog', 'weather-fog'),
+ new MeteoalarmEventInfo(MeteoalarmEventType.AirQuality, 'Air Quality', 'air-filter'),
+ new MeteoalarmEventInfo(MeteoalarmEventType.Unknown, 'Unknown Event', 'alert-circle-outline'),
];
}
static get levels(): MeteoalarmLevelInfo[] {
return [
- new MeteoalarmLevelInfo(MeteoalarmLevelType.Red, 'Red', '#db4437'),
+ new MeteoalarmLevelInfo(MeteoalarmLevelType.Red, 'Red', '#db4437'),
new MeteoalarmLevelInfo(MeteoalarmLevelType.Orange, 'Orange', '#EE5A24'),
new MeteoalarmLevelInfo(MeteoalarmLevelType.Yellow, 'Yellow', '#ff9800'),
- new MeteoalarmLevelInfo(MeteoalarmLevelType.None, 'None', 'inherit')
+ new MeteoalarmLevelInfo(MeteoalarmLevelType.None, 'None', 'inherit'),
];
}
diff --git a/src/editor-warnings.ts b/src/editor-warnings.ts
index 02a4b8e..9af55c7 100644
--- a/src/editor-warnings.ts
+++ b/src/editor-warnings.ts
@@ -5,27 +5,32 @@ import { MeteoalarmIntegration, MeteoalarmIntegrationEntityType } from './types'
export function generateEditorWarnings(
integration: MeteoalarmIntegration | undefined,
- entities: EntityConfig[] | undefined
+ entities: EntityConfig[] | undefined,
): TemplateResult {
// If entities are undefined, default to empty array
- if(!Array.isArray(entities)) entities = [];
- if(!integration) return html``;
+ if (!Array.isArray(entities)) entities = [];
+ if (!integration) return html``;
return html`
- ${duplicateWarning(entities)}
- ${missingExpectedEntityWarning(integration, entities)}
+ ${duplicateWarning(entities)} ${missingExpectedEntityWarning(integration, entities)}
${tooManyEntitiesWarning(integration, entities)}
- `;
+ `;
}
/**
* Generate warning for CurrentExpected integrations when second entity is not provided
*/
-function missingExpectedEntityWarning(integration: MeteoalarmIntegration, entities: EntityConfig[]): TemplateResult {
+function missingExpectedEntityWarning(
+ integration: MeteoalarmIntegration,
+ entities: EntityConfig[],
+): TemplateResult {
const validEntity = integration?.metadata.type == MeteoalarmIntegrationEntityType.CurrentExpected;
- if(validEntity && entities.length == 1) {
+ if (validEntity && entities.length == 1) {
return html`
-
+
${localize('editor.error.expected_entity')}
`;
@@ -37,14 +42,17 @@ function missingExpectedEntityWarning(integration: MeteoalarmIntegration, entiti
* Generate warning when user provides two identical entities in editor
*/
function duplicateWarning(entities: EntityConfig[]): TemplateResult {
- const uniqueEntities = Array.from(new Set(entities.map(x => x.entity)));
- const hasDuplicateEntities = uniqueEntities.length != entities.length;
- if(hasDuplicateEntities) {
+ const uniqueEntities = Array.from(new Set(entities.map((x) => x.entity)));
+ const hasDuplicateEntities = uniqueEntities.length != entities.length;
+ if (hasDuplicateEntities) {
return html`
-
+
${localize('editor.error.duplicate')}
- `;
+ `;
}
return html``;
}
@@ -52,17 +60,22 @@ function duplicateWarning(entities: EntityConfig[]): TemplateResult {
/**
* Generate warning when there are too much entities provided
*/
-function tooManyEntitiesWarning(integration: MeteoalarmIntegration, entities: EntityConfig[]): TemplateResult {
+function tooManyEntitiesWarning(
+ integration: MeteoalarmIntegration,
+ entities: EntityConfig[],
+): TemplateResult {
const shouldConsider = integration.metadata.entitiesCount > 0;
- if(shouldConsider && entities.length > integration.metadata.entitiesCount) {
+ if (shouldConsider && entities.length > integration.metadata.entitiesCount) {
return html`
-
- ${localize('editor.error.too_many_entities')
- .replace('{expected}', String(integration.metadata.entitiesCount))
- .replace('{got}', String(entities.length))}
+
+ ${localize('editor.error.too_many_entities')
+ .replace('{expected}', String(integration.metadata.entitiesCount))
+ .replace('{got}', String(entities.length))}
- `;
+ `;
}
return html``;
}
-
diff --git a/src/editor.ts b/src/editor.ts
index ba2b77d..6e1fbe9 100644
--- a/src/editor.ts
+++ b/src/editor.ts
@@ -11,302 +11,333 @@ import { generateEditorWarnings } from './editor-warnings';
import { processEditorEntities } from './helpers/process-editor-entities';
import { localize } from './localize/localize';
import { MeteoalarmCard } from './meteoalarm-card';
-import { MeteoalarmCardConfig, MeteoalarmIntegrationEntityType, MeteoalarmScalingMode } from './types';
+import {
+ MeteoalarmCardConfig,
+ MeteoalarmIntegrationEntityType,
+ MeteoalarmScalingMode,
+} from './types';
@customElement('meteoalarm-card-editor')
-export class MeteoalarmCardCardEditor extends ScopedRegistryHost(LitElement) implements LovelaceCardEditor {
- @property({ attribute: false }) public hass?: HomeAssistant;
- @state() private _config?: MeteoalarmCardConfig;
- @state() private _helpers?: any;
- private _initialized = false;
- @state() private _configEntities?: EntityConfig[];
-
- static elementDefinitions = {
- ...textfieldDefinition,
- ...selectDefinition,
- ...switchDefinition,
- ...formfieldDefinition
- };
-
- public setConfig(config: MeteoalarmCardConfig): void {
- this._config = config;
- this._configEntities = processEditorEntities(config.entities!);
- }
-
- protected firstUpdated(): void {
- this.loadLovelaceElements();
- }
-
- protected shouldUpdate(): boolean {
- if (!this._initialized) {
- this._initialize();
- }
- return true;
- }
-
- get _integration(): string {
- return this._config?.integration || '';
- }
-
- get _override_headline(): boolean {
- return this._config?.override_headline || false;
- }
-
- get _hide_when_no_warning(): boolean {
- return this._config?.hide_when_no_warning || false;
- }
-
- get _hide_caption(): boolean {
- return this._config?.hide_caption || false;
- }
-
- get _disable_swiper(): boolean {
- return this._config?.disable_swiper || false;
- }
-
- get _scaling_mode(): string {
- return this._config?.scaling_mode || 'headline_and_scale';
- }
-
- protected render(): TemplateResult | void {
- if (!this.hass) {
- return html``;
- }
-
- const integration = MeteoalarmCard.integrations.find(i => i.metadata.key === this._integration);
-
- return html`
-
- ${generateEditorWarnings(integration, this._configEntities)}
-
-
- ev.stopPropagation()}
- >
- ${MeteoalarmCard.integrations.map((integration) => {
- return html`${integration.metadata.name}`;
- })}
-
-
-
- ${integration?.metadata.type == MeteoalarmIntegrationEntityType.SingleEntity ? html`
- 0 ? this._configEntities![0].entity : ''}
- @value-changed=${this._valueChanged}
- >
- ` : html`
- ${localize('editor.entity')} (${localize('editor.required')})
-
- ${localize('editor.description.start')}
- ${' '}
- ${integration?.metadata.type == MeteoalarmIntegrationEntityType.CurrentExpected ? html`
+export class MeteoalarmCardCardEditor
+ extends ScopedRegistryHost(LitElement)
+ implements LovelaceCardEditor
+{
+ @property({ attribute: false }) public hass?: HomeAssistant;
+ @state() private _config?: MeteoalarmCardConfig;
+ @state() private _helpers?: any;
+ private _initialized = false;
+ @state() private _configEntities?: EntityConfig[];
+
+ static elementDefinitions = {
+ ...textfieldDefinition,
+ ...selectDefinition,
+ ...switchDefinition,
+ ...formfieldDefinition,
+ };
+
+ public setConfig(config: MeteoalarmCardConfig): void {
+ this._config = config;
+ this._configEntities = processEditorEntities(config.entities!);
+ }
+
+ protected firstUpdated(): void {
+ this.loadLovelaceElements();
+ }
+
+ protected shouldUpdate(): boolean {
+ if (!this._initialized) {
+ this._initialize();
+ }
+ return true;
+ }
+
+ get _integration(): string {
+ return this._config?.integration || '';
+ }
+
+ get _override_headline(): boolean {
+ return this._config?.override_headline || false;
+ }
+
+ get _hide_when_no_warning(): boolean {
+ return this._config?.hide_when_no_warning || false;
+ }
+
+ get _hide_caption(): boolean {
+ return this._config?.hide_caption || false;
+ }
+
+ get _disable_swiper(): boolean {
+ return this._config?.disable_swiper || false;
+ }
+
+ get _scaling_mode(): string {
+ return this._config?.scaling_mode || 'headline_and_scale';
+ }
+
+ protected render(): TemplateResult | void {
+ if (!this.hass) {
+ return html``;
+ }
+
+ const integration = MeteoalarmCard.integrations.find(
+ (i) => i.metadata.key === this._integration,
+ );
+
+ return html`
+
+ ${generateEditorWarnings(integration, this._configEntities)}
+
+
+ ev.stopPropagation()}
+ >
+ ${MeteoalarmCard.integrations.map((integration) => {
+ return html`${integration.metadata.name}`;
+ })}
+
+
+
+ ${integration?.metadata.type == MeteoalarmIntegrationEntityType.SingleEntity
+ ? html`
+ 0
+ ? this._configEntities![0].entity
+ : ''}
+ @value-changed=${this._valueChanged}
+ >
+ `
+ : html`
+
${localize('editor.entity')} (${localize('editor.required')})
+
+ ${localize('editor.description.start')} ${' '}
+ ${integration?.metadata.type == MeteoalarmIntegrationEntityType.CurrentExpected
+ ? html`
${localize('editor.description.current_expected')}
- ` : ''}
- ${integration?.metadata.type == MeteoalarmIntegrationEntityType.Slots ? html`
+ `
+ : ''}
+ ${integration?.metadata.type == MeteoalarmIntegrationEntityType.Slots
+ ? html`
${localize('editor.description.slots')}
- ` : ''}
- ${integration?.metadata.type == MeteoalarmIntegrationEntityType.WarningWatchStatementAdvisory ? html`
+ `
+ : ''}
+ ${integration?.metadata.type ==
+ MeteoalarmIntegrationEntityType.WarningWatchStatementAdvisory
+ ? html`
${localize('editor.description.warning_watch_statement_advisory')}
- ` : ''}
- ${integration?.metadata.type == MeteoalarmIntegrationEntityType.SeparateEvents ? html`
+ `
+ : ''}
+ ${integration?.metadata.type == MeteoalarmIntegrationEntityType.SeparateEvents
+ ? html`
${localize('editor.description.separate_events')}
- ` : ''}
- ${' '}
- ${localize('editor.description.end')}
-
-
-
- `}
+ `
+ : ''}
+ ${' '} ${localize('editor.description.end')}
+
+
+
+ `}
-
- ${integration?.metadata.returnMultipleAlerts? html`
-
-
-
- `: ''}
-
-
- ${integration?.metadata.returnHeadline ? html`
-
-
+
+ ${integration?.metadata.returnMultipleAlerts
+ ? html`
+
+
+
+ `
+ : ''}
+
+
+ ${integration?.metadata.returnHeadline
+ ? html`
+
+
+
+ `
+ : ''}
+
+
+ ${integration?.metadata.type == MeteoalarmIntegrationEntityType.CurrentExpected
+ ? html`
+
+
+
+ `
+ : ''}
+
+
+
+
- `: ''}
-
-
- ${integration?.metadata.type == MeteoalarmIntegrationEntityType.CurrentExpected? html`
-
-
-
- `: ''}
-
-
-
-
-
-
+
-
-
ev.stopPropagation()}
- >
- ${Object.values(MeteoalarmScalingMode).map((mode) => {
- return html`
-
+
-
- `;
- }
-
- private _initialize(): void {
- if (this.hass === undefined) return;
- if (this._config === undefined) return;
- if (this._helpers === undefined) return;
- this._initialized = true;
- }
-
- private async loadLovelaceElements(): Promise {
- // This function load card helpers and pre-loads all pre-made
- // custom elements from Lovelace like ha-entity-picker
- // Read more on why and code explanation:
- // Pre-loading elements:
- // https://github.com/thomasloven/hass-config/wiki/PreLoading-Lovelace-Elements
- // Pre-loading elements in ScopedRegistryHost:
- // https://gist.github.com/thomasloven/5f965bd26e5f69876890886c09dd9ba8
-
- const registry = (this.shadowRoot as any)?.customElements;
- if (!registry) return;
- if (registry.get('ha-entity-picker')) return;
-
- this._helpers = await (window as any).loadCardHelpers();
- const entitiesCard = await this._helpers.createCardElement({ type: 'entities', entities: [] });
- await entitiesCard.constructor.getConfigElement();
- const glanceCard = await this._helpers.createCardElement({ type: 'glance', entities: [] });
- await glanceCard.constructor.getConfigElement();
-
- registry.define('ha-entity-picker', window.customElements.get('ha-entity-picker'));
- registry.define('hui-entity-editor', window.customElements.get('hui-entity-editor'));
- registry.define('ha-alert', window.customElements.get('ha-alert'));
- }
-
- private _valueChanged(ev): void {
- if (!this._config || !this.hass) {
- return;
- }
- const target = ev.target;
- if (this[`_${target.configValue}`] === target.value) {
- return;
- }
- if (target.configValue) {
- if (target.value === '') {
- const tmpConfig = { ...this._config };
- delete tmpConfig[target.configValue];
- this._config = tmpConfig;
- }
- else {
- // Set value to config
- this._config = {
- ...this._config,
- [target.configValue]: target.checked !== undefined ? target.checked : target.value
- };
-
- // Convert entity format
- this._config = {
- ...this._config,
- entities: processEditorEntities(this._config.entities)
- };
-
- const integration = MeteoalarmCard.integrations.find(i => i.metadata.key === this._integration);
- if(integration?.metadata.type == MeteoalarmIntegrationEntityType.SingleEntity) {
- const entities = this._config.entities as EntityConfig[];
- this._config.entities = [entities[0]];
- }
- }
- }
- fireEvent(this, 'config-changed', { config: this._config });
- }
-
- private _entitiesChanged(ev: CustomEvent): void {
- // Change in entities from multiple entities selector
- let config = this._config!;
- config = { ...config, entities: ev.detail.entities! };
-
- this._configEntities = processEditorEntities(this._config!.entities!);
- fireEvent(this, 'config-changed', { config });
- }
-
- static styles: CSSResultGroup = css`
- mwc-select,
- mwc-textfield,
- ha-entity-picker,
- hui-entity-editor,
- ha-alert {
- margin-bottom: 16px;
- display: block;
+ `;
}
- mwc-formfield {
- padding-bottom: 8px;
+
+ private _initialize(): void {
+ if (this.hass === undefined) return;
+ if (this._config === undefined) return;
+ if (this._helpers === undefined) return;
+ this._initialized = true;
}
- mwc-switch {
- --mdc-theme-secondary: var(--switch-checked-color);
- --mdc-theme-surface: #999999;
+
+ private async loadLovelaceElements(): Promise {
+ // This function load card helpers and pre-loads all pre-made
+ // custom elements from Lovelace like ha-entity-picker
+ // Read more on why and code explanation:
+ // Pre-loading elements:
+ // https://github.com/thomasloven/hass-config/wiki/PreLoading-Lovelace-Elements
+ // Pre-loading elements in ScopedRegistryHost:
+ // https://gist.github.com/thomasloven/5f965bd26e5f69876890886c09dd9ba8
+
+ const registry = (this.shadowRoot as any)?.customElements;
+ if (!registry) return;
+ if (registry.get('ha-entity-picker')) return;
+
+ this._helpers = await (window as any).loadCardHelpers();
+ const entitiesCard = await this._helpers.createCardElement({ type: 'entities', entities: [] });
+ await entitiesCard.constructor.getConfigElement();
+ const glanceCard = await this._helpers.createCardElement({ type: 'glance', entities: [] });
+ await glanceCard.constructor.getConfigElement();
+
+ registry.define('ha-entity-picker', window.customElements.get('ha-entity-picker'));
+ registry.define('hui-entity-editor', window.customElements.get('hui-entity-editor'));
+ registry.define('ha-alert', window.customElements.get('ha-alert'));
}
- .options {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 18px;
- margin: 24px 0;
+
+ private _valueChanged(ev): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target;
+ if (this[`_${target.configValue}`] === target.value) {
+ return;
+ }
+ if (target.configValue) {
+ if (target.value === '') {
+ const tmpConfig = { ...this._config };
+ delete tmpConfig[target.configValue];
+ this._config = tmpConfig;
+ } else {
+ // Set value to config
+ this._config = {
+ ...this._config,
+ [target.configValue]: target.checked !== undefined ? target.checked : target.value,
+ };
+
+ // Convert entity format
+ this._config = {
+ ...this._config,
+ entities: processEditorEntities(this._config.entities),
+ };
+
+ const integration = MeteoalarmCard.integrations.find(
+ (i) => i.metadata.key === this._integration,
+ );
+ if (integration?.metadata.type == MeteoalarmIntegrationEntityType.SingleEntity) {
+ const entities = this._config.entities as EntityConfig[];
+ this._config.entities = [entities[0]];
+ }
+ }
+ }
+ fireEvent(this, 'config-changed', { config: this._config });
}
- p {
- max-width: 600px;
+
+ private _entitiesChanged(ev: CustomEvent): void {
+ // Change in entities from multiple entities selector
+ let config = this._config!;
+ config = { ...config, entities: ev.detail.entities! };
+
+ this._configEntities = processEditorEntities(this._config!.entities!);
+ fireEvent(this, 'config-changed', { config });
}
- `;
+
+ static styles: CSSResultGroup = css`
+ mwc-select,
+ mwc-textfield,
+ ha-entity-picker,
+ hui-entity-editor,
+ ha-alert {
+ margin-bottom: 16px;
+ display: block;
+ }
+ mwc-formfield {
+ padding-bottom: 8px;
+ }
+ mwc-switch {
+ --mdc-theme-secondary: var(--switch-checked-color);
+ --mdc-theme-surface: #999999;
+ }
+ .options {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 18px;
+ margin: 24px 0;
+ }
+ p {
+ max-width: 600px;
+ }
+ `;
}
diff --git a/src/events-praser.ts b/src/events-praser.ts
index 4862411..d53cf50 100644
--- a/src/events-praser.ts
+++ b/src/events-praser.ts
@@ -8,32 +8,30 @@ import {
MeteoalarmAlertParsed,
MeteoalarmEventType,
MeteoalarmIntegration,
- MeteoalarmIntegrationEntityType
+ MeteoalarmIntegrationEntityType,
} from './types';
/**
* This is the class that stands between integration and rendering code.
*/
class EventsParser {
- constructor(
- private integration: MeteoalarmIntegration
- ) {}
+ constructor(private integration: MeteoalarmIntegration) {}
/**
- * This function parses all of the provided entities and provides their attributes
- * to integration specified in constructors. The result is additionally processed and
- * verified then, packed to array of MeteoalarmAlertParsed objects
- */
+ * This function parses all of the provided entities and provides their attributes
+ * to integration specified in constructors. The result is additionally processed and
+ * verified then, packed to array of MeteoalarmAlertParsed objects
+ */
public getEvents(
entities: HassEntity[],
disableSweeper = false,
overrideHeadline = false,
hideCaption = false,
ignoredLevels: string[] = [],
- ignoredEvents: string[] = []
+ ignoredEvents: string[] = [],
): MeteoalarmAlertParsed[] {
- if(this.isAnyEntityUnavailable(entities)) {
- return [ PredefinedCards.unavailableCard() ];
+ if (this.isAnyEntityUnavailable(entities)) {
+ return [PredefinedCards.unavailableCard()];
}
this.checkIfIntegrationSupportsEntities(entities);
@@ -42,21 +40,21 @@ class EventsParser {
alerts = this.filterAlerts(alerts, ignoredLevels, ignoredEvents);
const result: MeteoalarmAlertParsed[] = [];
- for(const alert of alerts) {
+ for (const alert of alerts) {
const event = MeteoalarmData.getEvent(alert.event);
const level = MeteoalarmData.getLevel(alert.level);
const headlines = this.generateHeadlines(event, level);
// If there is provided headline, and user wants it, push it to headlines
- if(!overrideHeadline && alert.headline) {
+ if (!overrideHeadline && alert.headline) {
headlines.unshift(alert.headline);
}
let caption: string | undefined = undefined;
let captionIcon: string | undefined = undefined;
- if(!hideCaption) {
- if(alert.kind == MeteoalarmAlertKind.Expected) {
+ if (!hideCaption) {
+ if (alert.kind == MeteoalarmAlertKind.Expected) {
caption = localize('common.expected');
captionIcon = 'clock-outline';
}
@@ -69,37 +67,37 @@ class EventsParser {
color: level.color,
headlines: headlines,
caption: caption,
- captionIcon: captionIcon
+ captionIcon: captionIcon,
});
}
// If there are no results that mean above loop didn't trigger
// event parsing even once since every sensor was inactive.
- if(result.length == 0) {
- return [ PredefinedCards.noWarningsCard(entities[0]) ];
+ if (result.length == 0) {
+ return [PredefinedCards.noWarningsCard(entities[0])];
}
return disableSweeper ? result.slice(1) : result;
}
/**
- * Call integration for each of the entities and put all alerts in array
- */
+ * Call integration for each of the entities and put all alerts in array
+ */
private graterAllAlerts(entities: HassEntity[]): MeteoalarmAlert[] {
const alerts: MeteoalarmAlert[] = [];
- for(const entity of entities) {
+ for (const entity of entities) {
const active = this.integration.alertActive(entity);
- if(!active) continue;
+ if (!active) continue;
let entityAlerts = this.integration.getAlerts(entity);
- if(!Array.isArray(entityAlerts)) {
- entityAlerts = [ entityAlerts ];
+ if (!Array.isArray(entityAlerts)) {
+ entityAlerts = [entityAlerts];
}
- if(entityAlerts.length == 0) {
+ if (entityAlerts.length == 0) {
throw new Error('Integration is active but did not return any events');
}
- for(const alert of entityAlerts) {
- alerts.push({...alert, _entity: entity});
+ for (const alert of entityAlerts) {
+ alerts.push({ ...alert, _entity: entity });
}
}
return alerts;
@@ -108,15 +106,18 @@ class EventsParser {
private filterAlerts(
alerts: MeteoalarmAlert[],
ignoredLevels: string[],
- ignoredEvents: string[]
+ ignoredEvents: string[],
): MeteoalarmAlert[] {
- if(ignoredEvents.length == 0 && ignoredLevels.length == 0) return alerts;
+ if (ignoredEvents.length == 0 && ignoredLevels.length == 0) return alerts;
const result: MeteoalarmAlert[] = [];
- for(const alert of alerts) {
- const eventInfo = MeteoalarmData.events.find(e => e.type == alert.event)!;
- const levelInfo = MeteoalarmData.levels.find(e => e.type == alert.level)!;
- if(!ignoredEvents.includes(eventInfo.fullName) && !ignoredLevels.includes(levelInfo.fullName)) {
+ for (const alert of alerts) {
+ const eventInfo = MeteoalarmData.events.find((e) => e.type == alert.event)!;
+ const levelInfo = MeteoalarmData.levels.find((e) => e.type == alert.level)!;
+ if (
+ !ignoredEvents.includes(eventInfo.fullName) &&
+ !ignoredLevels.includes(levelInfo.fullName)
+ ) {
result.push(alert);
}
}
@@ -129,96 +130,103 @@ class EventsParser {
// Sort by how dangerous events are
alerts = alerts.sort((a, b) => {
const eventsData = MeteoalarmData.events;
- const aLevel = eventsData.indexOf(eventsData.find(e => e.type == a.event)!);
- const bLevel = eventsData.indexOf(eventsData.find(e => e.type == b.event)!);
- return(bLevel - aLevel);
+ const aLevel = eventsData.indexOf(eventsData.find((e) => e.type == a.event)!);
+ const bLevel = eventsData.indexOf(eventsData.find((e) => e.type == b.event)!);
+ return bLevel - aLevel;
});
// Sort by level
- alerts = alerts.sort((a, b) => (b.level - a.level));
+ alerts = alerts.sort((a, b) => b.level - a.level);
// Push expected events to back of the list
alerts = alerts.sort((a, b) => {
- if(a.kind === undefined) return 0;
- if(a.kind == MeteoalarmAlertKind.Current && b.kind == MeteoalarmAlertKind.Expected) return -1;
- else if(a.kind == MeteoalarmAlertKind.Expected && b.kind == MeteoalarmAlertKind.Current) return 1;
+ if (a.kind === undefined) return 0;
+ if (a.kind == MeteoalarmAlertKind.Current && b.kind == MeteoalarmAlertKind.Expected)
+ return -1;
+ else if (a.kind == MeteoalarmAlertKind.Expected && b.kind == MeteoalarmAlertKind.Current)
+ return 1;
return 0;
});
return alerts;
}
/**
- * Validate if specified alert is up to standards
- */
+ * Validate if specified alert is up to standards
+ */
private validateAlert(alerts: MeteoalarmAlert[]): void {
- for(const alert of alerts) {
- if(alert.event === undefined || alert.level === undefined) {
+ for (const alert of alerts) {
+ if (alert.event === undefined || alert.level === undefined) {
throw new Error(
- `[Alert QA Error] Invalid event object received: event: ${alert.event} level: ${alert.level}`);
+ `[Alert QA Error] Invalid event object received: event: ${alert.event} level: ${alert.level}`,
+ );
}
- if(!this.integration.metadata.returnHeadline && alert.headline) {
+ if (!this.integration.metadata.returnHeadline && alert.headline) {
throw new Error(
- '[Alert QA Error] metadata.returnHeadline is false but headline was returned');
+ '[Alert QA Error] metadata.returnHeadline is false but headline was returned',
+ );
}
- if(
- (this.integration.metadata.type == MeteoalarmIntegrationEntityType.CurrentExpected)
- && alert.kind == undefined
+ if (
+ this.integration.metadata.type == MeteoalarmIntegrationEntityType.CurrentExpected &&
+ alert.kind == undefined
) {
- throw new Error(
- '[Alert QA Error] CurrentExpected type is required to provide alert.kind');
+ throw new Error('[Alert QA Error] CurrentExpected type is required to provide alert.kind');
}
- if(
- (this.integration.metadata.type != MeteoalarmIntegrationEntityType.CurrentExpected)
- && alert.kind != undefined
+ if (
+ this.integration.metadata.type != MeteoalarmIntegrationEntityType.CurrentExpected &&
+ alert.kind != undefined
) {
- throw new Error(
- '[Alert QA Error] only CurrentExpected type can return alert.kind');
+ throw new Error('[Alert QA Error] only CurrentExpected type can return alert.kind');
}
- if(!this.integration.metadata.returnMultipleAlerts && alerts.length > 1) {
+ if (!this.integration.metadata.returnMultipleAlerts && alerts.length > 1) {
throw new Error(
- '[Alert QA Error] returnMultipleAlerts is false but more than one alert was returned');
+ '[Alert QA Error] returnMultipleAlerts is false but more than one alert was returned',
+ );
}
}
}
// Artificially generate headlines from event type and level
private generateHeadlines(event: MeteoalarmEventInfo, level: MeteoalarmLevelInfo): string[] {
- if(event.type === MeteoalarmEventType.Unknown) {
+ if (event.type === MeteoalarmEventType.Unknown) {
return [
localize(level.translationKey + '.generic'),
- localize(level.translationKey + '.color')
+ localize(level.translationKey + '.color'),
];
- }
- else {
+ } else {
const e = localize(event.translationKey);
return [
- localize(level.translationKey + '.event').replace('{event}', localize(event.translationKey)),
- e.charAt(0).toUpperCase() + e.slice(1)
+ localize(level.translationKey + '.event').replace(
+ '{event}',
+ localize(event.translationKey),
+ ),
+ e.charAt(0).toUpperCase() + e.slice(1),
];
}
}
private isAnyEntityUnavailable(entities: HassEntity[]): boolean {
- return entities.some(e => {
- return e == undefined || (e.attributes.status || e.attributes.state || e.state) === 'unavailable';
+ return entities.some((e) => {
+ return (
+ e == undefined || (e.attributes.status || e.attributes.state || e.state) === 'unavailable'
+ );
});
}
private checkIfIntegrationSupportsEntities(entities: HassEntity[]): void {
- if(!entities.every(e => this.integration.supports(e))) {
- if(entities.length == 1) {
+ if (!entities.every((e) => this.integration.supports(e))) {
+ if (entities.length == 1) {
throw new Error(localize('error.entity_invalid.single'));
- }
- else {
- const unsupportedEntities = entities.filter(e => !this.integration.supports(e));
+ } else {
+ const unsupportedEntities = entities.filter((e) => !this.integration.supports(e));
throw new Error(
- localize('error.entity_invalid.multiple')
- .replace('{entity}', unsupportedEntities.map(x => x.entity_id).join(', '))
+ localize('error.entity_invalid.multiple').replace(
+ '{entity}',
+ unsupportedEntities.map((x) => x.entity_id).join(', '),
+ ),
);
}
}
}
-
}
export default EventsParser;
diff --git a/src/external/swiperStyles.ts b/src/external/swiperStyles.ts
index 24b8d72..a5859fb 100644
--- a/src/external/swiperStyles.ts
+++ b/src/external/swiperStyles.ts
@@ -12,5 +12,616 @@
import { css } from 'lit';
export default css`
-@font-face{font-family:swiper-icons;src:url('data:application/font-woff;charset=utf-8;base64, d09GRgABAAAAAAZgABAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAGRAAAABoAAAAci6qHkUdERUYAAAWgAAAAIwAAACQAYABXR1BPUwAABhQAAAAuAAAANuAY7+xHU1VCAAAFxAAAAFAAAABm2fPczU9TLzIAAAHcAAAASgAAAGBP9V5RY21hcAAAAkQAAACIAAABYt6F0cBjdnQgAAACzAAAAAQAAAAEABEBRGdhc3AAAAWYAAAACAAAAAj//wADZ2x5ZgAAAywAAADMAAAD2MHtryVoZWFkAAABbAAAADAAAAA2E2+eoWhoZWEAAAGcAAAAHwAAACQC9gDzaG10eAAAAigAAAAZAAAArgJkABFsb2NhAAAC0AAAAFoAAABaFQAUGG1heHAAAAG8AAAAHwAAACAAcABAbmFtZQAAA/gAAAE5AAACXvFdBwlwb3N0AAAFNAAAAGIAAACE5s74hXjaY2BkYGAAYpf5Hu/j+W2+MnAzMYDAzaX6QjD6/4//Bxj5GA8AuRwMYGkAPywL13jaY2BkYGA88P8Agx4j+/8fQDYfA1AEBWgDAIB2BOoAeNpjYGRgYNBh4GdgYgABEMnIABJzYNADCQAACWgAsQB42mNgYfzCOIGBlYGB0YcxjYGBwR1Kf2WQZGhhYGBiYGVmgAFGBiQQkOaawtDAoMBQxXjg/wEGPcYDDA4wNUA2CCgwsAAAO4EL6gAAeNpj2M0gyAACqxgGNWBkZ2D4/wMA+xkDdgAAAHjaY2BgYGaAYBkGRgYQiAHyGMF8FgYHIM3DwMHABGQrMOgyWDLEM1T9/w8UBfEMgLzE////P/5//f/V/xv+r4eaAAeMbAxwIUYmIMHEgKYAYjUcsDAwsLKxc3BycfPw8jEQA/gZBASFhEVExcQlJKWkZWTl5BUUlZRVVNXUNTQZBgMAAMR+E+gAEQFEAAAAKgAqACoANAA+AEgAUgBcAGYAcAB6AIQAjgCYAKIArAC2AMAAygDUAN4A6ADyAPwBBgEQARoBJAEuATgBQgFMAVYBYAFqAXQBfgGIAZIBnAGmAbIBzgHsAAB42u2NMQ6CUAyGW568x9AneYYgm4MJbhKFaExIOAVX8ApewSt4Bic4AfeAid3VOBixDxfPYEza5O+Xfi04YADggiUIULCuEJK8VhO4bSvpdnktHI5QCYtdi2sl8ZnXaHlqUrNKzdKcT8cjlq+rwZSvIVczNiezsfnP/uznmfPFBNODM2K7MTQ45YEAZqGP81AmGGcF3iPqOop0r1SPTaTbVkfUe4HXj97wYE+yNwWYxwWu4v1ugWHgo3S1XdZEVqWM7ET0cfnLGxWfkgR42o2PvWrDMBSFj/IHLaF0zKjRgdiVMwScNRAoWUoH78Y2icB/yIY09An6AH2Bdu/UB+yxopYshQiEvnvu0dURgDt8QeC8PDw7Fpji3fEA4z/PEJ6YOB5hKh4dj3EvXhxPqH/SKUY3rJ7srZ4FZnh1PMAtPhwP6fl2PMJMPDgeQ4rY8YT6Gzao0eAEA409DuggmTnFnOcSCiEiLMgxCiTI6Cq5DZUd3Qmp10vO0LaLTd2cjN4fOumlc7lUYbSQcZFkutRG7g6JKZKy0RmdLY680CDnEJ+UMkpFFe1RN7nxdVpXrC4aTtnaurOnYercZg2YVmLN/d/gczfEimrE/fs/bOuq29Zmn8tloORaXgZgGa78yO9/cnXm2BpaGvq25Dv9S4E9+5SIc9PqupJKhYFSSl47+Qcr1mYNAAAAeNptw0cKwkAAAMDZJA8Q7OUJvkLsPfZ6zFVERPy8qHh2YER+3i/BP83vIBLLySsoKimrqKqpa2hp6+jq6RsYGhmbmJqZSy0sraxtbO3sHRydnEMU4uR6yx7JJXveP7WrDycAAAAAAAH//wACeNpjYGRgYOABYhkgZgJCZgZNBkYGLQZtIJsFLMYAAAw3ALgAeNolizEKgDAQBCchRbC2sFER0YD6qVQiBCv/H9ezGI6Z5XBAw8CBK/m5iQQVauVbXLnOrMZv2oLdKFa8Pjuru2hJzGabmOSLzNMzvutpB3N42mNgZGBg4GKQYzBhYMxJLMlj4GBgAYow/P/PAJJhLM6sSoWKfWCAAwDAjgbRAAB42mNgYGBkAIIbCZo5IPrmUn0hGA0AO8EFTQAA');font-weight:400;font-style:normal}:root{--swiper-theme-color:#007aff}.swiper{margin-left:auto;margin-right:auto;position:relative;overflow:hidden;list-style:none;padding:0;z-index:1}.swiper-vertical>.swiper-wrapper{flex-direction:column}.swiper-wrapper{position:relative;width:100%;height:100%;z-index:1;display:flex;transition-property:transform;box-sizing:content-box}.swiper-android .swiper-slide,.swiper-wrapper{transform:translate3d(0px,0,0)}.swiper-pointer-events{touch-action:pan-y}.swiper-pointer-events.swiper-vertical{touch-action:pan-x}.swiper-slide{flex-shrink:0;width:100%;height:100%;position:relative;transition-property:transform}.swiper-slide-invisible-blank{visibility:hidden}.swiper-autoheight,.swiper-autoheight .swiper-slide{height:auto}.swiper-autoheight .swiper-wrapper{align-items:flex-start;transition-property:transform,height}.swiper-backface-hidden .swiper-slide{transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden}.swiper-3d,.swiper-3d.swiper-css-mode .swiper-wrapper{perspective:1200px}.swiper-3d .swiper-cube-shadow,.swiper-3d .swiper-slide,.swiper-3d .swiper-slide-shadow,.swiper-3d .swiper-slide-shadow-bottom,.swiper-3d .swiper-slide-shadow-left,.swiper-3d .swiper-slide-shadow-right,.swiper-3d .swiper-slide-shadow-top,.swiper-3d .swiper-wrapper{transform-style:preserve-3d}.swiper-3d .swiper-slide-shadow,.swiper-3d .swiper-slide-shadow-bottom,.swiper-3d .swiper-slide-shadow-left,.swiper-3d .swiper-slide-shadow-right,.swiper-3d .swiper-slide-shadow-top{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:10}.swiper-3d .swiper-slide-shadow{background:rgba(0,0,0,.15)}.swiper-3d .swiper-slide-shadow-left{background-image:linear-gradient(to left,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-right{background-image:linear-gradient(to right,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-top{background-image:linear-gradient(to top,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-bottom{background-image:linear-gradient(to bottom,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-css-mode>.swiper-wrapper{overflow:auto;scrollbar-width:none;-ms-overflow-style:none}.swiper-css-mode>.swiper-wrapper::-webkit-scrollbar{display:none}.swiper-css-mode>.swiper-wrapper>.swiper-slide{scroll-snap-align:start start}.swiper-horizontal.swiper-css-mode>.swiper-wrapper{scroll-snap-type:x mandatory}.swiper-vertical.swiper-css-mode>.swiper-wrapper{scroll-snap-type:y mandatory}.swiper-centered>.swiper-wrapper::before{content:'';flex-shrink:0;order:9999}.swiper-centered.swiper-horizontal>.swiper-wrapper>.swiper-slide:first-child{margin-inline-start:var(--swiper-centered-offset-before)}.swiper-centered.swiper-horizontal>.swiper-wrapper::before{height:100%;min-height:1px;width:var(--swiper-centered-offset-after)}.swiper-centered.swiper-vertical>.swiper-wrapper>.swiper-slide:first-child{margin-block-start:var(--swiper-centered-offset-before)}.swiper-centered.swiper-vertical>.swiper-wrapper::before{width:100%;min-width:1px;height:var(--swiper-centered-offset-after)}.swiper-centered>.swiper-wrapper>.swiper-slide{scroll-snap-align:center center}.swiper-virtual .swiper-slide{-webkit-backface-visibility:hidden;transform:translateZ(0)}.swiper-virtual.swiper-css-mode .swiper-wrapper::after{content:'';position:absolute;left:0;top:0;pointer-events:none}.swiper-virtual.swiper-css-mode.swiper-horizontal .swiper-wrapper::after{height:1px;width:var(--swiper-virtual-size)}.swiper-virtual.swiper-css-mode.swiper-vertical .swiper-wrapper::after{width:1px;height:var(--swiper-virtual-size)}:root{--swiper-navigation-size:44px}.swiper-button-next,.swiper-button-prev{position:absolute;top:50%;width:calc(var(--swiper-navigation-size)/ 44 * 27);height:var(--swiper-navigation-size);margin-top:calc(0px - (var(--swiper-navigation-size)/ 2));z-index:10;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--swiper-navigation-color,var(--swiper-theme-color))}.swiper-button-next.swiper-button-disabled,.swiper-button-prev.swiper-button-disabled{opacity:.35;cursor:auto;pointer-events:none}.swiper-button-next.swiper-button-hidden,.swiper-button-prev.swiper-button-hidden{opacity:0;cursor:auto;pointer-events:none}.swiper-navigation-disabled .swiper-button-next,.swiper-navigation-disabled .swiper-button-prev{display:none!important}.swiper-button-next:after,.swiper-button-prev:after{font-family:swiper-icons;font-size:var(--swiper-navigation-size);text-transform:none!important;letter-spacing:0;font-variant:initial;line-height:1}.swiper-button-prev,.swiper-rtl .swiper-button-next{left:10px;right:auto}.swiper-button-prev:after,.swiper-rtl .swiper-button-next:after{content:'prev'}.swiper-button-next,.swiper-rtl .swiper-button-prev{right:10px;left:auto}.swiper-button-next:after,.swiper-rtl .swiper-button-prev:after{content:'next'}.swiper-button-lock{display:none}.swiper-pagination{position:absolute;text-align:center;transition:.3s opacity;transform:translate3d(0,0,0);z-index:10}.swiper-pagination.swiper-pagination-hidden{opacity:0}.swiper-pagination-disabled>.swiper-pagination,.swiper-pagination.swiper-pagination-disabled{display:none!important}.swiper-horizontal>.swiper-pagination-bullets,.swiper-pagination-bullets.swiper-pagination-horizontal,.swiper-pagination-custom,.swiper-pagination-fraction{bottom:10px;left:0;width:100%}.swiper-pagination-bullets-dynamic{overflow:hidden;font-size:0}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{transform:scale(.33);position:relative}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active{transform:scale(1)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-main{transform:scale(1)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev{transform:scale(.66)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev-prev{transform:scale(.33)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next{transform:scale(.66)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next-next{transform:scale(.33)}.swiper-pagination-bullet{width:var(--swiper-pagination-bullet-width,var(--swiper-pagination-bullet-size,8px));height:var(--swiper-pagination-bullet-height,var(--swiper-pagination-bullet-size,8px));display:inline-block;border-radius:50%;background:var(--swiper-pagination-bullet-inactive-color,#000);opacity:var(--swiper-pagination-bullet-inactive-opacity, .2)}button.swiper-pagination-bullet{border:none;margin:0;padding:0;box-shadow:none;-webkit-appearance:none;appearance:none}.swiper-pagination-clickable .swiper-pagination-bullet{cursor:pointer}.swiper-pagination-bullet:only-child{display:none!important}.swiper-pagination-bullet-active{opacity:var(--swiper-pagination-bullet-opacity, 1);background:var(--swiper-pagination-color,var(--swiper-theme-color))}.swiper-pagination-vertical.swiper-pagination-bullets,.swiper-vertical>.swiper-pagination-bullets{right:10px;top:50%;transform:translate3d(0px,-50%,0)}.swiper-pagination-vertical.swiper-pagination-bullets .swiper-pagination-bullet,.swiper-vertical>.swiper-pagination-bullets .swiper-pagination-bullet{margin:var(--swiper-pagination-bullet-vertical-gap,6px) 0;display:block}.swiper-pagination-vertical.swiper-pagination-bullets.swiper-pagination-bullets-dynamic,.swiper-vertical>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic{top:50%;transform:translateY(-50%);width:8px}.swiper-pagination-vertical.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet,.swiper-vertical>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{display:inline-block;transition:.2s transform,.2s top}.swiper-horizontal>.swiper-pagination-bullets .swiper-pagination-bullet,.swiper-pagination-horizontal.swiper-pagination-bullets .swiper-pagination-bullet{margin:0 var(--swiper-pagination-bullet-horizontal-gap,4px)}.swiper-horizontal>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic,.swiper-pagination-horizontal.swiper-pagination-bullets.swiper-pagination-bullets-dynamic{left:50%;transform:translateX(-50%);white-space:nowrap}.swiper-horizontal>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet,.swiper-pagination-horizontal.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{transition:.2s transform,.2s left}.swiper-horizontal.swiper-rtl>.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{transition:.2s transform,.2s right}.swiper-pagination-progressbar{background:rgba(0,0,0,.25);position:absolute}.swiper-pagination-progressbar .swiper-pagination-progressbar-fill{background:var(--swiper-pagination-color,var(--swiper-theme-color));position:absolute;left:0;top:0;width:100%;height:100%;transform:scale(0);transform-origin:left top}.swiper-rtl .swiper-pagination-progressbar .swiper-pagination-progressbar-fill{transform-origin:right top}.swiper-horizontal>.swiper-pagination-progressbar,.swiper-pagination-progressbar.swiper-pagination-horizontal,.swiper-pagination-progressbar.swiper-pagination-vertical.swiper-pagination-progressbar-opposite,.swiper-vertical>.swiper-pagination-progressbar.swiper-pagination-progressbar-opposite{width:100%;height:4px;left:0;top:0}.swiper-horizontal>.swiper-pagination-progressbar.swiper-pagination-progressbar-opposite,.swiper-pagination-progressbar.swiper-pagination-horizontal.swiper-pagination-progressbar-opposite,.swiper-pagination-progressbar.swiper-pagination-vertical,.swiper-vertical>.swiper-pagination-progressbar{width:4px;height:100%;left:0;top:0}.swiper-pagination-lock{display:none}.swiper-scrollbar{border-radius:10px;position:relative;-ms-touch-action:none;background:rgba(0,0,0,.1)}.swiper-scrollbar-disabled>.swiper-scrollbar,.swiper-scrollbar.swiper-scrollbar-disabled{display:none!important}.swiper-horizontal>.swiper-scrollbar{position:absolute;left:1%;bottom:3px;z-index:50;height:5px;width:98%}.swiper-vertical>.swiper-scrollbar{position:absolute;right:3px;top:1%;z-index:50;width:5px;height:98%}.swiper-scrollbar-drag{height:100%;width:100%;position:relative;background:rgba(0,0,0,.5);border-radius:10px;left:0;top:0}.swiper-scrollbar-cursor-drag{cursor:move}.swiper-scrollbar-lock{display:none}.swiper-zoom-container{width:100%;height:100%;display:flex;justify-content:center;align-items:center;text-align:center}.swiper-zoom-container>canvas,.swiper-zoom-container>img,.swiper-zoom-container>svg{max-width:100%;max-height:100%;object-fit:contain}.swiper-slide-zoomed{cursor:move}.swiper-lazy-preloader{width:42px;height:42px;position:absolute;left:50%;top:50%;margin-left:-21px;margin-top:-21px;z-index:10;transform-origin:50%;box-sizing:border-box;border:4px solid var(--swiper-preloader-color,var(--swiper-theme-color));border-radius:50%;border-top-color:transparent}.swiper-watch-progress .swiper-slide-visible .swiper-lazy-preloader,.swiper:not(.swiper-watch-progress) .swiper-lazy-preloader{animation:swiper-preloader-spin 1s infinite linear}.swiper-lazy-preloader-white{--swiper-preloader-color:#fff}.swiper-lazy-preloader-black{--swiper-preloader-color:#000}@keyframes swiper-preloader-spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.swiper .swiper-notification{position:absolute;left:0;top:0;pointer-events:none;opacity:0;z-index:-1000}.swiper-free-mode>.swiper-wrapper{transition-timing-function:ease-out;margin:0 auto}.swiper-grid>.swiper-wrapper{flex-wrap:wrap}.swiper-grid-column>.swiper-wrapper{flex-wrap:wrap;flex-direction:column}.swiper-fade.swiper-free-mode .swiper-slide{transition-timing-function:ease-out}.swiper-fade .swiper-slide{pointer-events:none;transition-property:opacity}.swiper-fade .swiper-slide .swiper-slide{pointer-events:none}.swiper-fade .swiper-slide-active,.swiper-fade .swiper-slide-active .swiper-slide-active{pointer-events:auto}.swiper-cube{overflow:visible}.swiper-cube .swiper-slide{pointer-events:none;-webkit-backface-visibility:hidden;backface-visibility:hidden;z-index:1;visibility:hidden;transform-origin:0 0;width:100%;height:100%}.swiper-cube .swiper-slide .swiper-slide{pointer-events:none}.swiper-cube.swiper-rtl .swiper-slide{transform-origin:100% 0}.swiper-cube .swiper-slide-active,.swiper-cube .swiper-slide-active .swiper-slide-active{pointer-events:auto}.swiper-cube .swiper-slide-active,.swiper-cube .swiper-slide-next,.swiper-cube .swiper-slide-next+.swiper-slide,.swiper-cube .swiper-slide-prev{pointer-events:auto;visibility:visible}.swiper-cube .swiper-slide-shadow-bottom,.swiper-cube .swiper-slide-shadow-left,.swiper-cube .swiper-slide-shadow-right,.swiper-cube .swiper-slide-shadow-top{z-index:0;-webkit-backface-visibility:hidden;backface-visibility:hidden}.swiper-cube .swiper-cube-shadow{position:absolute;left:0;bottom:0px;width:100%;height:100%;opacity:.6;z-index:0}.swiper-cube .swiper-cube-shadow:before{content:'';background:#000;position:absolute;left:0;top:0;bottom:0;right:0;filter:blur(50px)}.swiper-flip{overflow:visible}.swiper-flip .swiper-slide{pointer-events:none;-webkit-backface-visibility:hidden;backface-visibility:hidden;z-index:1}.swiper-flip .swiper-slide .swiper-slide{pointer-events:none}.swiper-flip .swiper-slide-active,.swiper-flip .swiper-slide-active .swiper-slide-active{pointer-events:auto}.swiper-flip .swiper-slide-shadow-bottom,.swiper-flip .swiper-slide-shadow-left,.swiper-flip .swiper-slide-shadow-right,.swiper-flip .swiper-slide-shadow-top{z-index:0;-webkit-backface-visibility:hidden;backface-visibility:hidden}.swiper-creative .swiper-slide{-webkit-backface-visibility:hidden;backface-visibility:hidden;overflow:hidden;transition-property:transform,opacity,height}.swiper-cards{overflow:visible}.swiper-cards .swiper-slide{transform-origin:center bottom;-webkit-backface-visibility:hidden;backface-visibility:hidden;overflow:hidden}
+ @font-face {
+ font-family: swiper-icons;
+ src: url('data:application/font-woff;charset=utf-8;base64, d09GRgABAAAAAAZgABAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAGRAAAABoAAAAci6qHkUdERUYAAAWgAAAAIwAAACQAYABXR1BPUwAABhQAAAAuAAAANuAY7+xHU1VCAAAFxAAAAFAAAABm2fPczU9TLzIAAAHcAAAASgAAAGBP9V5RY21hcAAAAkQAAACIAAABYt6F0cBjdnQgAAACzAAAAAQAAAAEABEBRGdhc3AAAAWYAAAACAAAAAj//wADZ2x5ZgAAAywAAADMAAAD2MHtryVoZWFkAAABbAAAADAAAAA2E2+eoWhoZWEAAAGcAAAAHwAAACQC9gDzaG10eAAAAigAAAAZAAAArgJkABFsb2NhAAAC0AAAAFoAAABaFQAUGG1heHAAAAG8AAAAHwAAACAAcABAbmFtZQAAA/gAAAE5AAACXvFdBwlwb3N0AAAFNAAAAGIAAACE5s74hXjaY2BkYGAAYpf5Hu/j+W2+MnAzMYDAzaX6QjD6/4//Bxj5GA8AuRwMYGkAPywL13jaY2BkYGA88P8Agx4j+/8fQDYfA1AEBWgDAIB2BOoAeNpjYGRgYNBh4GdgYgABEMnIABJzYNADCQAACWgAsQB42mNgYfzCOIGBlYGB0YcxjYGBwR1Kf2WQZGhhYGBiYGVmgAFGBiQQkOaawtDAoMBQxXjg/wEGPcYDDA4wNUA2CCgwsAAAO4EL6gAAeNpj2M0gyAACqxgGNWBkZ2D4/wMA+xkDdgAAAHjaY2BgYGaAYBkGRgYQiAHyGMF8FgYHIM3DwMHABGQrMOgyWDLEM1T9/w8UBfEMgLzE////P/5//f/V/xv+r4eaAAeMbAxwIUYmIMHEgKYAYjUcsDAwsLKxc3BycfPw8jEQA/gZBASFhEVExcQlJKWkZWTl5BUUlZRVVNXUNTQZBgMAAMR+E+gAEQFEAAAAKgAqACoANAA+AEgAUgBcAGYAcAB6AIQAjgCYAKIArAC2AMAAygDUAN4A6ADyAPwBBgEQARoBJAEuATgBQgFMAVYBYAFqAXQBfgGIAZIBnAGmAbIBzgHsAAB42u2NMQ6CUAyGW568x9AneYYgm4MJbhKFaExIOAVX8ApewSt4Bic4AfeAid3VOBixDxfPYEza5O+Xfi04YADggiUIULCuEJK8VhO4bSvpdnktHI5QCYtdi2sl8ZnXaHlqUrNKzdKcT8cjlq+rwZSvIVczNiezsfnP/uznmfPFBNODM2K7MTQ45YEAZqGP81AmGGcF3iPqOop0r1SPTaTbVkfUe4HXj97wYE+yNwWYxwWu4v1ugWHgo3S1XdZEVqWM7ET0cfnLGxWfkgR42o2PvWrDMBSFj/IHLaF0zKjRgdiVMwScNRAoWUoH78Y2icB/yIY09An6AH2Bdu/UB+yxopYshQiEvnvu0dURgDt8QeC8PDw7Fpji3fEA4z/PEJ6YOB5hKh4dj3EvXhxPqH/SKUY3rJ7srZ4FZnh1PMAtPhwP6fl2PMJMPDgeQ4rY8YT6Gzao0eAEA409DuggmTnFnOcSCiEiLMgxCiTI6Cq5DZUd3Qmp10vO0LaLTd2cjN4fOumlc7lUYbSQcZFkutRG7g6JKZKy0RmdLY680CDnEJ+UMkpFFe1RN7nxdVpXrC4aTtnaurOnYercZg2YVmLN/d/gczfEimrE/fs/bOuq29Zmn8tloORaXgZgGa78yO9/cnXm2BpaGvq25Dv9S4E9+5SIc9PqupJKhYFSSl47+Qcr1mYNAAAAeNptw0cKwkAAAMDZJA8Q7OUJvkLsPfZ6zFVERPy8qHh2YER+3i/BP83vIBLLySsoKimrqKqpa2hp6+jq6RsYGhmbmJqZSy0sraxtbO3sHRydnEMU4uR6yx7JJXveP7WrDycAAAAAAAH//wACeNpjYGRgYOABYhkgZgJCZgZNBkYGLQZtIJsFLMYAAAw3ALgAeNolizEKgDAQBCchRbC2sFER0YD6qVQiBCv/H9ezGI6Z5XBAw8CBK/m5iQQVauVbXLnOrMZv2oLdKFa8Pjuru2hJzGabmOSLzNMzvutpB3N42mNgZGBg4GKQYzBhYMxJLMlj4GBgAYow/P/PAJJhLM6sSoWKfWCAAwDAjgbRAAB42mNgYGBkAIIbCZo5IPrmUn0hGA0AO8EFTQAA');
+ font-weight: 400;
+ font-style: normal;
+ }
+ :root {
+ --swiper-theme-color: #007aff;
+ }
+ .swiper {
+ margin-left: auto;
+ margin-right: auto;
+ position: relative;
+ overflow: hidden;
+ list-style: none;
+ padding: 0;
+ z-index: 1;
+ }
+ .swiper-vertical > .swiper-wrapper {
+ flex-direction: column;
+ }
+ .swiper-wrapper {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ z-index: 1;
+ display: flex;
+ transition-property: transform;
+ box-sizing: content-box;
+ }
+ .swiper-android .swiper-slide,
+ .swiper-wrapper {
+ transform: translate3d(0px, 0, 0);
+ }
+ .swiper-pointer-events {
+ touch-action: pan-y;
+ }
+ .swiper-pointer-events.swiper-vertical {
+ touch-action: pan-x;
+ }
+ .swiper-slide {
+ flex-shrink: 0;
+ width: 100%;
+ height: 100%;
+ position: relative;
+ transition-property: transform;
+ }
+ .swiper-slide-invisible-blank {
+ visibility: hidden;
+ }
+ .swiper-autoheight,
+ .swiper-autoheight .swiper-slide {
+ height: auto;
+ }
+ .swiper-autoheight .swiper-wrapper {
+ align-items: flex-start;
+ transition-property: transform, height;
+ }
+ .swiper-backface-hidden .swiper-slide {
+ transform: translateZ(0);
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ }
+ .swiper-3d,
+ .swiper-3d.swiper-css-mode .swiper-wrapper {
+ perspective: 1200px;
+ }
+ .swiper-3d .swiper-cube-shadow,
+ .swiper-3d .swiper-slide,
+ .swiper-3d .swiper-slide-shadow,
+ .swiper-3d .swiper-slide-shadow-bottom,
+ .swiper-3d .swiper-slide-shadow-left,
+ .swiper-3d .swiper-slide-shadow-right,
+ .swiper-3d .swiper-slide-shadow-top,
+ .swiper-3d .swiper-wrapper {
+ transform-style: preserve-3d;
+ }
+ .swiper-3d .swiper-slide-shadow,
+ .swiper-3d .swiper-slide-shadow-bottom,
+ .swiper-3d .swiper-slide-shadow-left,
+ .swiper-3d .swiper-slide-shadow-right,
+ .swiper-3d .swiper-slide-shadow-top {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ z-index: 10;
+ }
+ .swiper-3d .swiper-slide-shadow {
+ background: rgba(0, 0, 0, 0.15);
+ }
+ .swiper-3d .swiper-slide-shadow-left {
+ background-image: linear-gradient(to left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
+ }
+ .swiper-3d .swiper-slide-shadow-right {
+ background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
+ }
+ .swiper-3d .swiper-slide-shadow-top {
+ background-image: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
+ }
+ .swiper-3d .swiper-slide-shadow-bottom {
+ background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
+ }
+ .swiper-css-mode > .swiper-wrapper {
+ overflow: auto;
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+ }
+ .swiper-css-mode > .swiper-wrapper::-webkit-scrollbar {
+ display: none;
+ }
+ .swiper-css-mode > .swiper-wrapper > .swiper-slide {
+ scroll-snap-align: start start;
+ }
+ .swiper-horizontal.swiper-css-mode > .swiper-wrapper {
+ scroll-snap-type: x mandatory;
+ }
+ .swiper-vertical.swiper-css-mode > .swiper-wrapper {
+ scroll-snap-type: y mandatory;
+ }
+ .swiper-centered > .swiper-wrapper::before {
+ content: '';
+ flex-shrink: 0;
+ order: 9999;
+ }
+ .swiper-centered.swiper-horizontal > .swiper-wrapper > .swiper-slide:first-child {
+ margin-inline-start: var(--swiper-centered-offset-before);
+ }
+ .swiper-centered.swiper-horizontal > .swiper-wrapper::before {
+ height: 100%;
+ min-height: 1px;
+ width: var(--swiper-centered-offset-after);
+ }
+ .swiper-centered.swiper-vertical > .swiper-wrapper > .swiper-slide:first-child {
+ margin-block-start: var(--swiper-centered-offset-before);
+ }
+ .swiper-centered.swiper-vertical > .swiper-wrapper::before {
+ width: 100%;
+ min-width: 1px;
+ height: var(--swiper-centered-offset-after);
+ }
+ .swiper-centered > .swiper-wrapper > .swiper-slide {
+ scroll-snap-align: center center;
+ }
+ .swiper-virtual .swiper-slide {
+ -webkit-backface-visibility: hidden;
+ transform: translateZ(0);
+ }
+ .swiper-virtual.swiper-css-mode .swiper-wrapper::after {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 0;
+ pointer-events: none;
+ }
+ .swiper-virtual.swiper-css-mode.swiper-horizontal .swiper-wrapper::after {
+ height: 1px;
+ width: var(--swiper-virtual-size);
+ }
+ .swiper-virtual.swiper-css-mode.swiper-vertical .swiper-wrapper::after {
+ width: 1px;
+ height: var(--swiper-virtual-size);
+ }
+ :root {
+ --swiper-navigation-size: 44px;
+ }
+ .swiper-button-next,
+ .swiper-button-prev {
+ position: absolute;
+ top: 50%;
+ width: calc(var(--swiper-navigation-size) / 44 * 27);
+ height: var(--swiper-navigation-size);
+ margin-top: calc(0px - (var(--swiper-navigation-size) / 2));
+ z-index: 10;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--swiper-navigation-color, var(--swiper-theme-color));
+ }
+ .swiper-button-next.swiper-button-disabled,
+ .swiper-button-prev.swiper-button-disabled {
+ opacity: 0.35;
+ cursor: auto;
+ pointer-events: none;
+ }
+ .swiper-button-next.swiper-button-hidden,
+ .swiper-button-prev.swiper-button-hidden {
+ opacity: 0;
+ cursor: auto;
+ pointer-events: none;
+ }
+ .swiper-navigation-disabled .swiper-button-next,
+ .swiper-navigation-disabled .swiper-button-prev {
+ display: none !important;
+ }
+ .swiper-button-next:after,
+ .swiper-button-prev:after {
+ font-family: swiper-icons;
+ font-size: var(--swiper-navigation-size);
+ text-transform: none !important;
+ letter-spacing: 0;
+ font-variant: initial;
+ line-height: 1;
+ }
+ .swiper-button-prev,
+ .swiper-rtl .swiper-button-next {
+ left: 10px;
+ right: auto;
+ }
+ .swiper-button-prev:after,
+ .swiper-rtl .swiper-button-next:after {
+ content: 'prev';
+ }
+ .swiper-button-next,
+ .swiper-rtl .swiper-button-prev {
+ right: 10px;
+ left: auto;
+ }
+ .swiper-button-next:after,
+ .swiper-rtl .swiper-button-prev:after {
+ content: 'next';
+ }
+ .swiper-button-lock {
+ display: none;
+ }
+ .swiper-pagination {
+ position: absolute;
+ text-align: center;
+ transition: 0.3s opacity;
+ transform: translate3d(0, 0, 0);
+ z-index: 10;
+ }
+ .swiper-pagination.swiper-pagination-hidden {
+ opacity: 0;
+ }
+ .swiper-pagination-disabled > .swiper-pagination,
+ .swiper-pagination.swiper-pagination-disabled {
+ display: none !important;
+ }
+ .swiper-horizontal > .swiper-pagination-bullets,
+ .swiper-pagination-bullets.swiper-pagination-horizontal,
+ .swiper-pagination-custom,
+ .swiper-pagination-fraction {
+ bottom: 10px;
+ left: 0;
+ width: 100%;
+ }
+ .swiper-pagination-bullets-dynamic {
+ overflow: hidden;
+ font-size: 0;
+ }
+ .swiper-pagination-bullets-dynamic .swiper-pagination-bullet {
+ transform: scale(0.33);
+ position: relative;
+ }
+ .swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active {
+ transform: scale(1);
+ }
+ .swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-main {
+ transform: scale(1);
+ }
+ .swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev {
+ transform: scale(0.66);
+ }
+ .swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev-prev {
+ transform: scale(0.33);
+ }
+ .swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next {
+ transform: scale(0.66);
+ }
+ .swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next-next {
+ transform: scale(0.33);
+ }
+ .swiper-pagination-bullet {
+ width: var(--swiper-pagination-bullet-width, var(--swiper-pagination-bullet-size, 8px));
+ height: var(--swiper-pagination-bullet-height, var(--swiper-pagination-bullet-size, 8px));
+ display: inline-block;
+ border-radius: 50%;
+ background: var(--swiper-pagination-bullet-inactive-color, #000);
+ opacity: var(--swiper-pagination-bullet-inactive-opacity, 0.2);
+ }
+ button.swiper-pagination-bullet {
+ border: none;
+ margin: 0;
+ padding: 0;
+ box-shadow: none;
+ -webkit-appearance: none;
+ appearance: none;
+ }
+ .swiper-pagination-clickable .swiper-pagination-bullet {
+ cursor: pointer;
+ }
+ .swiper-pagination-bullet:only-child {
+ display: none !important;
+ }
+ .swiper-pagination-bullet-active {
+ opacity: var(--swiper-pagination-bullet-opacity, 1);
+ background: var(--swiper-pagination-color, var(--swiper-theme-color));
+ }
+ .swiper-pagination-vertical.swiper-pagination-bullets,
+ .swiper-vertical > .swiper-pagination-bullets {
+ right: 10px;
+ top: 50%;
+ transform: translate3d(0px, -50%, 0);
+ }
+ .swiper-pagination-vertical.swiper-pagination-bullets .swiper-pagination-bullet,
+ .swiper-vertical > .swiper-pagination-bullets .swiper-pagination-bullet {
+ margin: var(--swiper-pagination-bullet-vertical-gap, 6px) 0;
+ display: block;
+ }
+ .swiper-pagination-vertical.swiper-pagination-bullets.swiper-pagination-bullets-dynamic,
+ .swiper-vertical > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic {
+ top: 50%;
+ transform: translateY(-50%);
+ width: 8px;
+ }
+ .swiper-pagination-vertical.swiper-pagination-bullets.swiper-pagination-bullets-dynamic
+ .swiper-pagination-bullet,
+ .swiper-vertical
+ > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic
+ .swiper-pagination-bullet {
+ display: inline-block;
+ transition:
+ 0.2s transform,
+ 0.2s top;
+ }
+ .swiper-horizontal > .swiper-pagination-bullets .swiper-pagination-bullet,
+ .swiper-pagination-horizontal.swiper-pagination-bullets .swiper-pagination-bullet {
+ margin: 0 var(--swiper-pagination-bullet-horizontal-gap, 4px);
+ }
+ .swiper-horizontal > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic,
+ .swiper-pagination-horizontal.swiper-pagination-bullets.swiper-pagination-bullets-dynamic {
+ left: 50%;
+ transform: translateX(-50%);
+ white-space: nowrap;
+ }
+ .swiper-horizontal
+ > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic
+ .swiper-pagination-bullet,
+ .swiper-pagination-horizontal.swiper-pagination-bullets.swiper-pagination-bullets-dynamic
+ .swiper-pagination-bullet {
+ transition:
+ 0.2s transform,
+ 0.2s left;
+ }
+ .swiper-horizontal.swiper-rtl > .swiper-pagination-bullets-dynamic .swiper-pagination-bullet {
+ transition:
+ 0.2s transform,
+ 0.2s right;
+ }
+ .swiper-pagination-progressbar {
+ background: rgba(0, 0, 0, 0.25);
+ position: absolute;
+ }
+ .swiper-pagination-progressbar .swiper-pagination-progressbar-fill {
+ background: var(--swiper-pagination-color, var(--swiper-theme-color));
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ transform: scale(0);
+ transform-origin: left top;
+ }
+ .swiper-rtl .swiper-pagination-progressbar .swiper-pagination-progressbar-fill {
+ transform-origin: right top;
+ }
+ .swiper-horizontal > .swiper-pagination-progressbar,
+ .swiper-pagination-progressbar.swiper-pagination-horizontal,
+ .swiper-pagination-progressbar.swiper-pagination-vertical.swiper-pagination-progressbar-opposite,
+ .swiper-vertical > .swiper-pagination-progressbar.swiper-pagination-progressbar-opposite {
+ width: 100%;
+ height: 4px;
+ left: 0;
+ top: 0;
+ }
+ .swiper-horizontal > .swiper-pagination-progressbar.swiper-pagination-progressbar-opposite,
+ .swiper-pagination-progressbar.swiper-pagination-horizontal.swiper-pagination-progressbar-opposite,
+ .swiper-pagination-progressbar.swiper-pagination-vertical,
+ .swiper-vertical > .swiper-pagination-progressbar {
+ width: 4px;
+ height: 100%;
+ left: 0;
+ top: 0;
+ }
+ .swiper-pagination-lock {
+ display: none;
+ }
+ .swiper-scrollbar {
+ border-radius: 10px;
+ position: relative;
+ -ms-touch-action: none;
+ background: rgba(0, 0, 0, 0.1);
+ }
+ .swiper-scrollbar-disabled > .swiper-scrollbar,
+ .swiper-scrollbar.swiper-scrollbar-disabled {
+ display: none !important;
+ }
+ .swiper-horizontal > .swiper-scrollbar {
+ position: absolute;
+ left: 1%;
+ bottom: 3px;
+ z-index: 50;
+ height: 5px;
+ width: 98%;
+ }
+ .swiper-vertical > .swiper-scrollbar {
+ position: absolute;
+ right: 3px;
+ top: 1%;
+ z-index: 50;
+ width: 5px;
+ height: 98%;
+ }
+ .swiper-scrollbar-drag {
+ height: 100%;
+ width: 100%;
+ position: relative;
+ background: rgba(0, 0, 0, 0.5);
+ border-radius: 10px;
+ left: 0;
+ top: 0;
+ }
+ .swiper-scrollbar-cursor-drag {
+ cursor: move;
+ }
+ .swiper-scrollbar-lock {
+ display: none;
+ }
+ .swiper-zoom-container {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ text-align: center;
+ }
+ .swiper-zoom-container > canvas,
+ .swiper-zoom-container > img,
+ .swiper-zoom-container > svg {
+ max-width: 100%;
+ max-height: 100%;
+ object-fit: contain;
+ }
+ .swiper-slide-zoomed {
+ cursor: move;
+ }
+ .swiper-lazy-preloader {
+ width: 42px;
+ height: 42px;
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ margin-left: -21px;
+ margin-top: -21px;
+ z-index: 10;
+ transform-origin: 50%;
+ box-sizing: border-box;
+ border: 4px solid var(--swiper-preloader-color, var(--swiper-theme-color));
+ border-radius: 50%;
+ border-top-color: transparent;
+ }
+ .swiper-watch-progress .swiper-slide-visible .swiper-lazy-preloader,
+ .swiper:not(.swiper-watch-progress) .swiper-lazy-preloader {
+ animation: swiper-preloader-spin 1s infinite linear;
+ }
+ .swiper-lazy-preloader-white {
+ --swiper-preloader-color: #fff;
+ }
+ .swiper-lazy-preloader-black {
+ --swiper-preloader-color: #000;
+ }
+ @keyframes swiper-preloader-spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+ }
+ .swiper .swiper-notification {
+ position: absolute;
+ left: 0;
+ top: 0;
+ pointer-events: none;
+ opacity: 0;
+ z-index: -1000;
+ }
+ .swiper-free-mode > .swiper-wrapper {
+ transition-timing-function: ease-out;
+ margin: 0 auto;
+ }
+ .swiper-grid > .swiper-wrapper {
+ flex-wrap: wrap;
+ }
+ .swiper-grid-column > .swiper-wrapper {
+ flex-wrap: wrap;
+ flex-direction: column;
+ }
+ .swiper-fade.swiper-free-mode .swiper-slide {
+ transition-timing-function: ease-out;
+ }
+ .swiper-fade .swiper-slide {
+ pointer-events: none;
+ transition-property: opacity;
+ }
+ .swiper-fade .swiper-slide .swiper-slide {
+ pointer-events: none;
+ }
+ .swiper-fade .swiper-slide-active,
+ .swiper-fade .swiper-slide-active .swiper-slide-active {
+ pointer-events: auto;
+ }
+ .swiper-cube {
+ overflow: visible;
+ }
+ .swiper-cube .swiper-slide {
+ pointer-events: none;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ z-index: 1;
+ visibility: hidden;
+ transform-origin: 0 0;
+ width: 100%;
+ height: 100%;
+ }
+ .swiper-cube .swiper-slide .swiper-slide {
+ pointer-events: none;
+ }
+ .swiper-cube.swiper-rtl .swiper-slide {
+ transform-origin: 100% 0;
+ }
+ .swiper-cube .swiper-slide-active,
+ .swiper-cube .swiper-slide-active .swiper-slide-active {
+ pointer-events: auto;
+ }
+ .swiper-cube .swiper-slide-active,
+ .swiper-cube .swiper-slide-next,
+ .swiper-cube .swiper-slide-next + .swiper-slide,
+ .swiper-cube .swiper-slide-prev {
+ pointer-events: auto;
+ visibility: visible;
+ }
+ .swiper-cube .swiper-slide-shadow-bottom,
+ .swiper-cube .swiper-slide-shadow-left,
+ .swiper-cube .swiper-slide-shadow-right,
+ .swiper-cube .swiper-slide-shadow-top {
+ z-index: 0;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ }
+ .swiper-cube .swiper-cube-shadow {
+ position: absolute;
+ left: 0;
+ bottom: 0px;
+ width: 100%;
+ height: 100%;
+ opacity: 0.6;
+ z-index: 0;
+ }
+ .swiper-cube .swiper-cube-shadow:before {
+ content: '';
+ background: #000;
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ right: 0;
+ filter: blur(50px);
+ }
+ .swiper-flip {
+ overflow: visible;
+ }
+ .swiper-flip .swiper-slide {
+ pointer-events: none;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ z-index: 1;
+ }
+ .swiper-flip .swiper-slide .swiper-slide {
+ pointer-events: none;
+ }
+ .swiper-flip .swiper-slide-active,
+ .swiper-flip .swiper-slide-active .swiper-slide-active {
+ pointer-events: auto;
+ }
+ .swiper-flip .swiper-slide-shadow-bottom,
+ .swiper-flip .swiper-slide-shadow-left,
+ .swiper-flip .swiper-slide-shadow-right,
+ .swiper-flip .swiper-slide-shadow-top {
+ z-index: 0;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ }
+ .swiper-creative .swiper-slide {
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ overflow: hidden;
+ transition-property: transform, opacity, height;
+ }
+ .swiper-cards {
+ overflow: visible;
+ }
+ .swiper-cards .swiper-slide {
+ transform-origin: center bottom;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ overflow: hidden;
+ }
`;
diff --git a/src/helpers/action-handler-directive.ts b/src/helpers/action-handler-directive.ts
index 02a1460..6614bc6 100644
--- a/src/helpers/action-handler-directive.ts
+++ b/src/helpers/action-handler-directive.ts
@@ -3,166 +3,165 @@ import { ActionHandlerDetail, ActionHandlerOptions } from 'custom-card-helpers/d
import { noChange } from 'lit';
import { AttributePart, directive, Directive, DirectiveParameters } from 'lit/directive';
-const isTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.maxTouchPoints > 0;
+const isTouch =
+ 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.maxTouchPoints > 0;
interface ActionHandler extends HTMLElement {
- holdTime: number;
- bind(element: Element, options): void;
+ holdTime: number;
+ bind(element: Element, options): void;
}
interface ActionHandlerElement extends HTMLElement {
- actionHandler?: boolean;
+ actionHandler?: boolean;
}
declare global {
- interface HASSDomEvents {
- action: ActionHandlerDetail;
- }
+ interface HASSDomEvents {
+ action: ActionHandlerDetail;
+ }
}
class ActionHandler extends HTMLElement implements ActionHandler {
- public holdTime = 500;
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- public ripple: any;
-
- protected timer?: number;
-
- protected held = false;
-
- private dblClickTimeout?: number;
-
- constructor() {
- super();
- this.ripple = document.createElement('mwc-ripple');
- }
-
- public connectedCallback(): void {
- Object.assign(this.style, {
- position: 'absolute',
- width: isTouch ? '100px' : '50px',
- height: isTouch ? '100px' : '50px',
- transform: 'translate(-50%, -50%)',
- pointerEvents: 'none',
- zIndex: '999'
- });
-
- this.appendChild(this.ripple);
- this.ripple.primary = true;
-
- ['touchcancel', 'mouseout', 'mouseup', 'touchmove', 'mousewheel', 'wheel', 'scroll'].forEach((ev) => {
- document.addEventListener(
- ev,
- () => {
- clearTimeout(this.timer);
- this.stopAnimation();
- this.timer = undefined;
- },
- { passive: true }
- );
- });
- }
-
- public bind(element: ActionHandlerElement, options): void {
- if (element.actionHandler) {
- return;
- }
- element.actionHandler = true;
-
- element.addEventListener('contextmenu', (ev: Event) => {
- const e = ev || window.event;
- if (e.preventDefault) {
- e.preventDefault();
- }
- if (e.stopPropagation) {
- e.stopPropagation();
- }
- e.cancelBubble = true;
- e.returnValue = false;
- return false;
- });
-
- const start = (ev: Event): void => {
- this.held = false;
- let x;
- let y;
- if ((ev as TouchEvent).touches) {
- x = (ev as TouchEvent).touches[0].pageX;
- y = (ev as TouchEvent).touches[0].pageY;
- }
- else {
- x = (ev as MouseEvent).pageX;
- y = (ev as MouseEvent).pageY;
- }
-
- this.timer = window.setTimeout(() => {
- this.startAnimation(x, y);
- this.held = true;
- }, this.holdTime);
- };
-
- const end = (ev: Event): void => {
- // Prevent mouse event if touch event
- ev.preventDefault();
- if (['touchend', 'touchcancel'].includes(ev.type) && this.timer === undefined) {
- return;
- }
- clearTimeout(this.timer);
- this.stopAnimation();
- this.timer = undefined;
- if (this.held) {
- fireEvent(element, 'action', { action: 'hold' });
- }
- else if (options.hasDoubleClick) {
- if ((ev.type === 'click' && (ev as MouseEvent).detail < 2) || !this.dblClickTimeout) {
- this.dblClickTimeout = window.setTimeout(() => {
- this.dblClickTimeout = undefined;
- fireEvent(element, 'action', { action: 'tap' });
- }, 250);
- }
- else {
- clearTimeout(this.dblClickTimeout);
- this.dblClickTimeout = undefined;
- fireEvent(element, 'action', { action: 'double_tap' });
- }
- }
- else {
- fireEvent(element, 'action', { action: 'tap' });
- }
- };
-
- const handleEnter = (ev: KeyboardEvent): void => {
- if (ev.keyCode !== 13) {
- return;
- }
- end(ev);
- };
-
- element.addEventListener('touchstart', start, { passive: true });
- element.addEventListener('touchend', end);
- element.addEventListener('touchcancel', end);
-
- element.addEventListener('mousedown', start, { passive: true });
- element.addEventListener('click', end);
-
- element.addEventListener('keyup', handleEnter);
- }
-
- private startAnimation(x: number, y: number): void {
- Object.assign(this.style, {
- left: `${x}px`,
- top: `${y}px`,
- display: null
- });
- this.ripple.disabled = false;
- this.ripple.active = true;
- this.ripple.unbounded = true;
- }
-
- private stopAnimation(): void {
- this.ripple.active = false;
- this.ripple.disabled = true;
- this.style.display = 'none';
- }
+ public holdTime = 500;
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ public ripple: any;
+
+ protected timer?: number;
+
+ protected held = false;
+
+ private dblClickTimeout?: number;
+
+ constructor() {
+ super();
+ this.ripple = document.createElement('mwc-ripple');
+ }
+
+ public connectedCallback(): void {
+ Object.assign(this.style, {
+ position: 'absolute',
+ width: isTouch ? '100px' : '50px',
+ height: isTouch ? '100px' : '50px',
+ transform: 'translate(-50%, -50%)',
+ pointerEvents: 'none',
+ zIndex: '999',
+ });
+
+ this.appendChild(this.ripple);
+ this.ripple.primary = true;
+
+ ['touchcancel', 'mouseout', 'mouseup', 'touchmove', 'mousewheel', 'wheel', 'scroll'].forEach(
+ (ev) => {
+ document.addEventListener(
+ ev,
+ () => {
+ clearTimeout(this.timer);
+ this.stopAnimation();
+ this.timer = undefined;
+ },
+ { passive: true },
+ );
+ },
+ );
+ }
+
+ public bind(element: ActionHandlerElement, options): void {
+ if (element.actionHandler) {
+ return;
+ }
+ element.actionHandler = true;
+
+ element.addEventListener('contextmenu', (ev: Event) => {
+ const e = ev || window.event;
+ if (e.preventDefault) {
+ e.preventDefault();
+ }
+ if (e.stopPropagation) {
+ e.stopPropagation();
+ }
+ e.cancelBubble = true;
+ e.returnValue = false;
+ return false;
+ });
+
+ const start = (ev: Event): void => {
+ this.held = false;
+ let x;
+ let y;
+ if ((ev as TouchEvent).touches) {
+ x = (ev as TouchEvent).touches[0].pageX;
+ y = (ev as TouchEvent).touches[0].pageY;
+ } else {
+ x = (ev as MouseEvent).pageX;
+ y = (ev as MouseEvent).pageY;
+ }
+
+ this.timer = window.setTimeout(() => {
+ this.startAnimation(x, y);
+ this.held = true;
+ }, this.holdTime);
+ };
+
+ const end = (ev: Event): void => {
+ // Prevent mouse event if touch event
+ ev.preventDefault();
+ if (['touchend', 'touchcancel'].includes(ev.type) && this.timer === undefined) {
+ return;
+ }
+ clearTimeout(this.timer);
+ this.stopAnimation();
+ this.timer = undefined;
+ if (this.held) {
+ fireEvent(element, 'action', { action: 'hold' });
+ } else if (options.hasDoubleClick) {
+ if ((ev.type === 'click' && (ev as MouseEvent).detail < 2) || !this.dblClickTimeout) {
+ this.dblClickTimeout = window.setTimeout(() => {
+ this.dblClickTimeout = undefined;
+ fireEvent(element, 'action', { action: 'tap' });
+ }, 250);
+ } else {
+ clearTimeout(this.dblClickTimeout);
+ this.dblClickTimeout = undefined;
+ fireEvent(element, 'action', { action: 'double_tap' });
+ }
+ } else {
+ fireEvent(element, 'action', { action: 'tap' });
+ }
+ };
+
+ const handleEnter = (ev: KeyboardEvent): void => {
+ if (ev.keyCode !== 13) {
+ return;
+ }
+ end(ev);
+ };
+
+ element.addEventListener('touchstart', start, { passive: true });
+ element.addEventListener('touchend', end);
+ element.addEventListener('touchcancel', end);
+
+ element.addEventListener('mousedown', start, { passive: true });
+ element.addEventListener('click', end);
+
+ element.addEventListener('keyup', handleEnter);
+ }
+
+ private startAnimation(x: number, y: number): void {
+ Object.assign(this.style, {
+ left: `${x}px`,
+ top: `${y}px`,
+ display: null,
+ });
+ this.ripple.disabled = false;
+ this.ripple.active = true;
+ this.ripple.unbounded = true;
+ }
+
+ private stopAnimation(): void {
+ this.ripple.active = false;
+ this.ripple.disabled = true;
+ this.style.display = 'none';
+ }
}
customElements.define('action-handler-meteoalarm', ActionHandler);
@@ -179,7 +178,10 @@ const getActionHandler = (): ActionHandler => {
return actionhandler as ActionHandler;
};
-export const actionHandlerBind = (element: ActionHandlerElement, options?: ActionHandlerOptions): void => {
+export const actionHandlerBind = (
+ element: ActionHandlerElement,
+ options?: ActionHandlerOptions,
+): void => {
const actionhandler: ActionHandler = getActionHandler();
if (!actionhandler) {
return;
@@ -196,5 +198,5 @@ export const actionHandler = directive(
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
render(_options?: ActionHandlerOptions) {}
- }
+ },
);
diff --git a/src/helpers/process-config-entities.ts b/src/helpers/process-config-entities.ts
index 8d41a03..7806dbf 100644
--- a/src/helpers/process-config-entities.ts
+++ b/src/helpers/process-config-entities.ts
@@ -3,41 +3,33 @@
import { EntityConfig } from 'custom-card-helpers';
-export function processConfigEntities(entities: Array | string | EntityConfig): EntityConfig[] {
+export function processConfigEntities(
+ entities: Array | string | EntityConfig,
+): EntityConfig[] {
if (!Array.isArray(entities)) {
- entities = [ entities ];
+ entities = [entities];
}
- if(entities.length > 0 && entities.every(e => e == null)) {
+ if (entities.length > 0 && entities.every((e) => e == null)) {
return [];
}
- return entities.map(
- (entityConf, index): any=> {
- if (
- typeof entityConf === 'object' &&
- !Array.isArray(entityConf) &&
- entityConf.type
- ) {
- return entityConf;
- }
+ return entities.map((entityConf, index): any => {
+ if (typeof entityConf === 'object' && !Array.isArray(entityConf) && entityConf.type) {
+ return entityConf;
+ }
- let config: EntityConfig;
+ let config: EntityConfig;
- if (typeof entityConf === 'string') {
- config = { entity: entityConf } as EntityConfig;
- }
- else if (typeof entityConf === 'object' && !Array.isArray(entityConf)) {
- if (!('entity' in entityConf)) {
- throw new Error(
- `Entity object at position ${index} is missing entity field.`
- );
- }
- config = entityConf as EntityConfig;
- }
- else {
- throw new Error(`Invalid entity specified at position ${index}.`);
+ if (typeof entityConf === 'string') {
+ config = { entity: entityConf } as EntityConfig;
+ } else if (typeof entityConf === 'object' && !Array.isArray(entityConf)) {
+ if (!('entity' in entityConf)) {
+ throw new Error(`Entity object at position ${index} is missing entity field.`);
}
- return config;
+ config = entityConf as EntityConfig;
+ } else {
+ throw new Error(`Invalid entity specified at position ${index}.`);
}
- );
+ return config;
+ });
}
diff --git a/src/helpers/process-editor-entities.ts b/src/helpers/process-editor-entities.ts
index 5b95df3..b543648 100644
--- a/src/helpers/process-editor-entities.ts
+++ b/src/helpers/process-editor-entities.ts
@@ -5,13 +5,13 @@ import { EntityConfig } from 'custom-card-helpers';
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function processEditorEntities(entities: Array | string | undefined): EntityConfig[] {
- if(entities == undefined) {
+ if (entities == undefined) {
return [];
}
- if(!Array.isArray(entities)) {
- entities = [ entities ];
+ if (!Array.isArray(entities)) {
+ entities = [entities];
}
- if(entities.length > 0 && entities.every(e => e == null)) {
+ if (entities.length > 0 && entities.every((e) => e == null)) {
return [];
}
return entities.map((entityConf: any) => {
diff --git a/src/integrations/burze_dzis_net.ts b/src/integrations/burze_dzis_net.ts
index ced9c8c..73904ab 100644
--- a/src/integrations/burze_dzis_net.ts
+++ b/src/integrations/burze_dzis_net.ts
@@ -4,16 +4,16 @@ import {
MeteoalarmEventType,
MeteoalarmIntegration,
MeteoalarmIntegrationEntityType,
- MeteoalarmIntegrationMetadata
+ MeteoalarmIntegrationMetadata,
} from '../types';
type BurzeDzisNetEntity = HassEntity & {
attributes: {
- attribution: string,
- description?: string,
- level?: number
- }
-}
+ attribution: string;
+ description?: string;
+ level?: number;
+ };
+};
export default class BurzeDzisNet implements MeteoalarmIntegration {
public get metadata(): MeteoalarmIntegrationMetadata {
@@ -30,15 +30,16 @@ export default class BurzeDzisNet implements MeteoalarmIntegration {
MeteoalarmEventType.Rain,
MeteoalarmEventType.Thunderstorms,
MeteoalarmEventType.Tornado,
- MeteoalarmEventType.Wind
- ]
+ MeteoalarmEventType.Wind,
+ ],
};
}
public supports(entity: BurzeDzisNetEntity): boolean {
return (
entity.attributes.attribution == 'Information provided by Burze.dzis.net.' &&
- this.getEventType(entity) !== undefined);
+ this.getEventType(entity) !== undefined
+ );
}
public alertActive(entity: BurzeDzisNetEntity): boolean {
@@ -50,27 +51,40 @@ export default class BurzeDzisNet implements MeteoalarmIntegration {
return {
event: event,
level: entity.attributes.level!,
- headline: entity.attributes.description
+ headline: entity.attributes.description,
};
}
private getEventType(entity: HassEntity): MeteoalarmEventType | undefined {
- if(entity.entity_id.endsWith('frost_warning') && entity.attributes.friendly_name?.endsWith('Ostrzeżenie - Mróz')) {
+ if (
+ entity.entity_id.endsWith('frost_warning') &&
+ entity.attributes.friendly_name?.endsWith('Ostrzeżenie - Mróz')
+ ) {
return MeteoalarmEventType.LowTemperature;
- }
- else if(entity.entity_id.endsWith('heat_warning') && entity.attributes.friendly_name?.endsWith('Ostrzeżenie - Upał')) {
+ } else if (
+ entity.entity_id.endsWith('heat_warning') &&
+ entity.attributes.friendly_name?.endsWith('Ostrzeżenie - Upał')
+ ) {
return MeteoalarmEventType.HighTemperature;
- }
- else if(entity.entity_id.endsWith('precipitation_warning') && entity.attributes.friendly_name?.endsWith('Ostrzeżenie - Opad')) {
+ } else if (
+ entity.entity_id.endsWith('precipitation_warning') &&
+ entity.attributes.friendly_name?.endsWith('Ostrzeżenie - Opad')
+ ) {
return MeteoalarmEventType.Rain;
- }
- else if(entity.entity_id.endsWith('storm_warning') && entity.attributes.friendly_name?.endsWith('Ostrzeżenie - Burza')) {
+ } else if (
+ entity.entity_id.endsWith('storm_warning') &&
+ entity.attributes.friendly_name?.endsWith('Ostrzeżenie - Burza')
+ ) {
return MeteoalarmEventType.Thunderstorms;
- }
- else if(entity.entity_id.endsWith('tornado_warning') && entity.attributes.friendly_name?.endsWith('Ostrzeżenie - Trąba')) {
+ } else if (
+ entity.entity_id.endsWith('tornado_warning') &&
+ entity.attributes.friendly_name?.endsWith('Ostrzeżenie - Trąba')
+ ) {
return MeteoalarmEventType.Tornado;
- }
- else if(entity.entity_id.endsWith('wind_warning') && entity.attributes.friendly_name?.endsWith(' Ostrzeżenie - Wiatr')) {
+ } else if (
+ entity.entity_id.endsWith('wind_warning') &&
+ entity.attributes.friendly_name?.endsWith(' Ostrzeżenie - Wiatr')
+ ) {
return MeteoalarmEventType.Wind;
}
return undefined;
diff --git a/src/integrations/dwd.ts b/src/integrations/dwd.ts
index d038152..cbb1488 100644
--- a/src/integrations/dwd.ts
+++ b/src/integrations/dwd.ts
@@ -6,16 +6,16 @@ import {
MeteoalarmIntegration,
MeteoalarmIntegrationEntityType,
MeteoalarmIntegrationMetadata,
- MeteoalarmLevelType
+ MeteoalarmLevelType,
} from '../types';
import { Utils } from '../utils';
type DWDEntity = HassEntity & {
attributes: {
- attribution: string,
- warning_count: number
- }
-}
+ attribution: string;
+ warning_count: number;
+ };
+};
export default class DWD implements MeteoalarmIntegration {
public get metadata(): MeteoalarmIntegrationMetadata {
@@ -26,12 +26,15 @@ export default class DWD implements MeteoalarmIntegration {
returnHeadline: true,
returnMultipleAlerts: true,
entitiesCount: 2,
- monitoredConditions: Utils.convertEventTypesForMetadata(this.eventTypes)
+ monitoredConditions: Utils.convertEventTypesForMetadata(this.eventTypes),
};
}
public supports(entity: DWDEntity): boolean {
- return entity.attributes.attribution == 'Data provided by DWD' && this.getEntityKind(entity) !== undefined;
+ return (
+ entity.attributes.attribution == 'Data provided by DWD' &&
+ this.getEntityKind(entity) !== undefined
+ );
}
public alertActive(entity: DWDEntity): boolean {
@@ -102,7 +105,7 @@ export default class DWD implements MeteoalarmIntegration {
15: MeteoalarmEventType.CoastalEvent,
16: MeteoalarmEventType.CoastalEvent,
57: MeteoalarmEventType.CoastalEvent,
- 58: MeteoalarmEventType.CoastalEvent
+ 58: MeteoalarmEventType.CoastalEvent,
};
}
@@ -116,19 +119,17 @@ export default class DWD implements MeteoalarmIntegration {
const level = entity.attributes[`warning_${i}_level`];
const id = entity.attributes[`warning_${i}_type`];
const headline = entity.attributes[`warning_${i}_headline`];
- if(level == entity.state) {
- if(id in this.eventTypes) {
+ if (level == entity.state) {
+ if (id in this.eventTypes) {
result.push({
- headline: headline,
+ headline: headline,
level: this.convertAwarenessLevel(level) as MeteoalarmLevelType,
event: this.eventTypes[id],
- kind: kind
+ kind: kind,
});
- }
- else if(id == 98 || id == 99) {
+ } else if (id == 98 || id == 99) {
throw new Error('An test warning was issued! ID: ' + id);
- }
- else {
+ } else {
throw new Error('Unknown event ID: ' + id);
}
}
@@ -143,16 +144,13 @@ export default class DWD implements MeteoalarmIntegration {
}
private getEntityKind(entity: HassEntity): MeteoalarmAlertKind | undefined {
- if(entity.entity_id.split('_').includes('current')) {
+ if (entity.entity_id.split('_').includes('current')) {
return MeteoalarmAlertKind.Current;
- }
- else if(entity.entity_id.split('_').includes('advance')) {
+ } else if (entity.entity_id.split('_').includes('advance')) {
return MeteoalarmAlertKind.Expected;
- }
- else if(entity.attributes.friendly_name?.split(' ').includes('Current')) {
+ } else if (entity.attributes.friendly_name?.split(' ').includes('Current')) {
return MeteoalarmAlertKind.Current;
- }
- else if(entity.attributes.friendly_name?.split(' ').includes('Advance')) {
+ } else if (entity.attributes.friendly_name?.split(' ').includes('Advance')) {
return MeteoalarmAlertKind.Expected;
}
return undefined;
diff --git a/src/integrations/env_canada.ts b/src/integrations/env_canada.ts
index 8ea97c2..2dc5a2b 100644
--- a/src/integrations/env_canada.ts
+++ b/src/integrations/env_canada.ts
@@ -5,20 +5,20 @@ import {
MeteoalarmIntegration,
MeteoalarmIntegrationEntityType,
MeteoalarmIntegrationMetadata,
- MeteoalarmLevelType
+ MeteoalarmLevelType,
} from '../types';
type EnvCanadaEntity = HassEntity & {
attributes: {
- attribution: string
- }
-}
+ attribution: string;
+ };
+};
export enum EnvCanadaEntityType {
Warning,
Watch,
Statement,
- Advisory
+ Advisory,
}
const ATTRIBUTION_EN = 'Data provided by Environment Canada';
@@ -33,7 +33,7 @@ export default class EnvironmentCanada implements MeteoalarmIntegration {
returnHeadline: false,
returnMultipleAlerts: true,
entitiesCount: 4,
- monitoredConditions: [...new Set(this.eventTypes.map(e => e.type))]
+ monitoredConditions: [...new Set(this.eventTypes.map((e) => e.type))],
};
}
@@ -42,174 +42,175 @@ export default class EnvironmentCanada implements MeteoalarmIntegration {
return (
[ATTRIBUTION_EN, ATTRIBUTION_FR].includes(entity.attributes.attribution) &&
this.getEntityType(entity) !== undefined &&
- isStateNumber);
+ isStateNumber
+ );
}
public alertActive(entity: EnvCanadaEntity): boolean {
return Number(entity.state) > 0;
}
- private get eventTypes(): {en: string, fr: string, type: MeteoalarmEventType }[] {
+ private get eventTypes(): { en: string; fr: string; type: MeteoalarmEventType }[] {
// English from: https://www.canada.ca/en/environment-climate-change/services/types-weather-forecasts-use/public/criteria-alerts.html
// French from : https://www.canada.ca/fr/environnement-changement-climatique/services/types-previsions-meteorologiques-utilisation/publiques/criteres-alertes-meteo.html
return [
{
en: 'Arctic Outflow',
fr: 'Poussée d’air Arctique',
- type: MeteoalarmEventType.SnowIce
+ type: MeteoalarmEventType.SnowIce,
},
{
en: 'Blizzard',
fr: 'Blizzard',
- type: MeteoalarmEventType.SnowIce
+ type: MeteoalarmEventType.SnowIce,
},
{
en: 'Blowing Snow',
fr: 'Poudrerie',
- type: MeteoalarmEventType.SnowIce
+ type: MeteoalarmEventType.SnowIce,
},
{
en: 'Dust Storm',
fr: 'Tempête de Poussière',
- type: MeteoalarmEventType.Dust
+ type: MeteoalarmEventType.Dust,
},
{
en: 'Extreme Cold',
fr: 'Froid Extrême',
- type: MeteoalarmEventType.LowTemperature
+ type: MeteoalarmEventType.LowTemperature,
},
{
en: 'Flash Freeze',
fr: 'Refroidissement Soudain',
- type: MeteoalarmEventType.SnowIce
+ type: MeteoalarmEventType.SnowIce,
},
{
en: 'Fog',
fr: 'Brouillard',
- type: MeteoalarmEventType.Fog
+ type: MeteoalarmEventType.Fog,
},
{
en: 'Freezing Drizzle',
fr: 'Bruine Verglaçante',
- type: MeteoalarmEventType.SnowIce
+ type: MeteoalarmEventType.SnowIce,
},
{
en: 'Freezing Rain',
fr: 'Pluie Verglaçante',
- type: MeteoalarmEventType.SnowIce
+ type: MeteoalarmEventType.SnowIce,
},
{
en: 'Frost',
fr: 'Gel',
- type: MeteoalarmEventType.SnowIce
+ type: MeteoalarmEventType.SnowIce,
},
{
en: 'Heat',
fr: 'Chaleur',
- type: MeteoalarmEventType.HighTemperature
+ type: MeteoalarmEventType.HighTemperature,
},
{
en: 'Hurricane',
fr: 'Ouragan',
- type: MeteoalarmEventType.Hurricane
+ type: MeteoalarmEventType.Hurricane,
},
{
en: 'Rainfall',
fr: 'Pluie',
- type: MeteoalarmEventType.Rain
+ type: MeteoalarmEventType.Rain,
},
{
en: 'Severe Thunderstorm',
fr: 'Orage Violent',
- type: MeteoalarmEventType.Thunderstorms
+ type: MeteoalarmEventType.Thunderstorms,
},
{
en: 'Smog',
fr: 'Smog',
- type: MeteoalarmEventType.AirQuality
+ type: MeteoalarmEventType.AirQuality,
},
{
en: 'Snowfall',
fr: 'Neige',
- type: MeteoalarmEventType.SnowIce
+ type: MeteoalarmEventType.SnowIce,
},
{
en: 'Snow Squall',
fr: 'Bourrasques de Neige',
- type: MeteoalarmEventType.SnowIce
+ type: MeteoalarmEventType.SnowIce,
},
{
en: 'Storm Surge',
fr: 'Onde de Tempête',
- type: MeteoalarmEventType.Thunderstorms
+ type: MeteoalarmEventType.Thunderstorms,
},
{
en: 'Tornado',
fr: 'Tornade',
- type: MeteoalarmEventType.Tornado
+ type: MeteoalarmEventType.Tornado,
},
{
en: 'Tropical Storm',
fr: 'Tempête Tropicale',
- type: MeteoalarmEventType.Hurricane
+ type: MeteoalarmEventType.Hurricane,
},
{
en: 'Tsunami',
fr: 'Tsunami',
- type: MeteoalarmEventType.Tsunami
+ type: MeteoalarmEventType.Tsunami,
},
{
en: 'Weather',
fr: 'Météorologique',
- type: MeteoalarmEventType.Unknown
+ type: MeteoalarmEventType.Unknown,
},
{
en: 'Wind',
fr: 'Vents',
- type: MeteoalarmEventType.Wind
+ type: MeteoalarmEventType.Wind,
},
{
en: 'Winter Storm',
fr: 'Tempête Hivernale',
- type: MeteoalarmEventType.SnowIce
+ type: MeteoalarmEventType.SnowIce,
},
{
en: 'Special Weather',
fr: 'Météorologique Spécial',
- type: MeteoalarmEventType.Unknown
+ type: MeteoalarmEventType.Unknown,
},
{
en: 'Special Air Quality',
- fr: 'Spécial Sur La Qualité De L\'Air',
- type: MeteoalarmEventType.AirQuality
- }
+ fr: "Spécial Sur La Qualité De L'Air",
+ type: MeteoalarmEventType.AirQuality,
+ },
];
}
- private get entityTypeTranslation(): {en: string, fr: string, type: EnvCanadaEntityType }[] {
+ private get entityTypeTranslation(): { en: string; fr: string; type: EnvCanadaEntityType }[] {
// English from: https://www.canada.ca/en/environment-climate-change/services/types-weather-forecasts-use/public/criteria-alerts.html
// French from : https://www.canada.ca/fr/environnement-changement-climatique/services/types-previsions-meteorologiques-utilisation/publiques/criteres-alertes-meteo.html
return [
{
type: EnvCanadaEntityType.Warning,
en: 'Warning',
- fr: 'Avertissement De'
+ fr: 'Avertissement De',
},
{
type: EnvCanadaEntityType.Watch,
en: 'Watch',
- fr: 'Veille De'
+ fr: 'Veille De',
},
{
type: EnvCanadaEntityType.Statement,
en: 'Statement',
- fr: 'Bulletin'
+ fr: 'Bulletin',
},
{
type: EnvCanadaEntityType.Advisory,
en: 'Advisory',
- fr: 'Avis De'
- }
+ fr: 'Avis De',
+ },
];
}
@@ -220,19 +221,18 @@ export default class EnvironmentCanada implements MeteoalarmIntegration {
* This transforms this back to event type
*/
private praseAlertName(alertName: string, type: EnvCanadaEntityType, isFrench: boolean) {
- const prefixTranslation = this.entityTypeTranslation.find(t => t.type == type)!;
+ const prefixTranslation = this.entityTypeTranslation.find((t) => t.type == type)!;
const prefix = isFrench ? prefixTranslation.fr : prefixTranslation.en;
- if(!alertName.includes(prefix)) {
- throw new Error(`Translated event prefix was not found in alert name '${prefix}' (isFrench=${isFrench})`);
+ if (!alertName.includes(prefix)) {
+ throw new Error(
+ `Translated event prefix was not found in alert name '${prefix}' (isFrench=${isFrench})`,
+ );
}
alertName = alertName.replace(prefix, '').trim();
- return this.eventTypes.find(e => {
- return (
- (isFrench && e.fr == alertName) ||
- (!isFrench && e.en == alertName)
- );
+ return this.eventTypes.find((e) => {
+ return (isFrench && e.fr == alertName) || (!isFrench && e.en == alertName);
});
}
@@ -247,13 +247,12 @@ export default class EnvironmentCanada implements MeteoalarmIntegration {
const isFrench = entity.attributes.attribution == 'Données fournies par Environnement Canada';
const alert = this.praseAlertName(alertName, type, isFrench);
- if(alert) {
+ if (alert) {
result.push({
level: this.getLevelFromType(type),
- event: alert.type
+ event: alert.type,
});
- }
- else {
+ } else {
throw new Error(`Unknown Env canada alert type: ${alertName} (isFrench=${isFrench})`);
}
}
@@ -264,18 +263,18 @@ export default class EnvironmentCanada implements MeteoalarmIntegration {
private getEntityType(entity: HassEntity): EnvCanadaEntityType | undefined {
// It's actually a sketchy solution to this, entity type can be detected by
// entity_id or friendly_name so it loops thought both of them
- for(const attribute of [ entity.entity_id, entity.attributes.friendly_name?.toLocaleLowerCase() ]) {
- if(!attribute) continue;
- if(attribute.includes('warnings')) {
+ for (const attribute of [
+ entity.entity_id,
+ entity.attributes.friendly_name?.toLocaleLowerCase(),
+ ]) {
+ if (!attribute) continue;
+ if (attribute.includes('warnings')) {
return EnvCanadaEntityType.Warning;
- }
- else if(attribute.includes('watches')) {
+ } else if (attribute.includes('watches')) {
return EnvCanadaEntityType.Watch;
- }
- else if(attribute.includes('statements')) {
+ } else if (attribute.includes('statements')) {
return EnvCanadaEntityType.Statement;
- }
- else if(attribute.includes('advisory')) {
+ } else if (attribute.includes('advisory')) {
return EnvCanadaEntityType.Advisory;
}
}
@@ -283,16 +282,13 @@ export default class EnvironmentCanada implements MeteoalarmIntegration {
}
private getLevelFromType(type: EnvCanadaEntityType): MeteoalarmLevelType {
- if(type == EnvCanadaEntityType.Warning) {
+ if (type == EnvCanadaEntityType.Warning) {
return MeteoalarmLevelType.Red;
- }
- else if(type == EnvCanadaEntityType.Watch) {
+ } else if (type == EnvCanadaEntityType.Watch) {
return MeteoalarmLevelType.Orange;
- }
- else if(type == EnvCanadaEntityType.Statement || type == EnvCanadaEntityType.Advisory) {
+ } else if (type == EnvCanadaEntityType.Statement || type == EnvCanadaEntityType.Advisory) {
return MeteoalarmLevelType.Yellow;
- }
- else {
+ } else {
// This should never happen but is required in order for TS to stop complaining
return MeteoalarmLevelType.Red;
}
diff --git a/src/integrations/integrations.ts b/src/integrations/integrations.ts
index 0e89197..71dc3c3 100644
--- a/src/integrations/integrations.ts
+++ b/src/integrations/integrations.ts
@@ -13,7 +13,7 @@ const INTEGRATIONS = [
NINA,
EnvironmentCanada,
BurzeDzisNet,
- Weatheralerts
+ Weatheralerts,
];
export default INTEGRATIONS;
diff --git a/src/integrations/meteoalarm.ts b/src/integrations/meteoalarm.ts
index 8d1a0bc..bac8658 100644
--- a/src/integrations/meteoalarm.ts
+++ b/src/integrations/meteoalarm.ts
@@ -5,7 +5,7 @@ import {
MeteoalarmIntegration,
MeteoalarmIntegrationEntityType,
MeteoalarmIntegrationMetadata,
- MeteoalarmLevelType
+ MeteoalarmLevelType,
} from '../types';
import { Utils } from '../utils';
@@ -15,16 +15,16 @@ type MeteoalarmEntity = HassEntity & {
// Only awareness_level and awareness_type: https://github.com/MrBartusek/MeteoalarmCard/issues/49
// awareness_level and awareness_type not present: https://github.com/MrBartusek/MeteoalarmCard/issues/48
// code should except that everything or nothing will be there
- awareness_level?: string,
- awareness_type?: string,
- event?: string,
- severity?: string,
- headline?: string,
- description?: string,
- icon: string,
- attribution: string
- }
-}
+ awareness_level?: string;
+ awareness_type?: string;
+ event?: string;
+ severity?: string;
+ headline?: string;
+ description?: string;
+ icon: string;
+ attribution: string;
+ };
+};
export default class Meteoalarm implements MeteoalarmIntegration {
public get metadata(): MeteoalarmIntegrationMetadata {
@@ -35,7 +35,7 @@ export default class Meteoalarm implements MeteoalarmIntegration {
returnHeadline: true,
returnMultipleAlerts: false,
entitiesCount: 1,
- monitoredConditions: this.eventTypes
+ monitoredConditions: this.eventTypes,
};
}
@@ -61,7 +61,7 @@ export default class Meteoalarm implements MeteoalarmIntegration {
MeteoalarmEventType.Avalanches,
MeteoalarmEventType.Rain,
MeteoalarmEventType.Flooding,
- MeteoalarmEventType.Flooding
+ MeteoalarmEventType.Flooding,
];
}
@@ -71,36 +71,38 @@ export default class Meteoalarm implements MeteoalarmIntegration {
headline,
severity,
awareness_type: awarenessType,
- awareness_level: awarenessLevel
+ awareness_level: awarenessLevel,
} = entity.attributes;
let event: MeteoalarmEventType | undefined;
let level: MeteoalarmLevelType | undefined;
- if(awarenessType != undefined) {
+ if (awarenessType != undefined) {
event = this.eventTypes[Number(awarenessType.split(';')[0]) - 1];
}
- if(awarenessLevel != undefined) {
+ if (awarenessLevel != undefined) {
let levelID = Number(awarenessLevel.split(';')[0]);
- if(levelID == 1) {
+ if (levelID == 1) {
// Fallback for https://github.com/MrBartusek/MeteoalarmCard/issues/49
levelID = 2;
}
- level = levelID - 1 as MeteoalarmLevelType;
+ level = (levelID - 1) as MeteoalarmLevelType;
}
- if(level === undefined && severity !== undefined) {
+ if (level === undefined && severity !== undefined) {
level = Utils.getLevelBySeverity(severity);
}
- if(level === undefined) {
+ if (level === undefined) {
throw new Error('Failed to determine alert level. awareness_level nor severity are provided');
}
- return [{
- headline: eventHeadline || headline,
- level: level,
- event: event || MeteoalarmEventType.Unknown
- }];
+ return [
+ {
+ headline: eventHeadline || headline,
+ level: level,
+ event: event || MeteoalarmEventType.Unknown,
+ },
+ ];
}
}
diff --git a/src/integrations/meteofrance.ts b/src/integrations/meteofrance.ts
index 4c7b468..71c8821 100644
--- a/src/integrations/meteofrance.ts
+++ b/src/integrations/meteofrance.ts
@@ -5,43 +5,43 @@ import {
MeteoalarmIntegration,
MeteoalarmIntegrationEntityType,
MeteoalarmIntegrationMetadata,
- MeteoalarmLevelType
+ MeteoalarmLevelType,
} from '../types';
-const STATE_GREEN = 'Vert';
+const STATE_GREEN = 'Vert';
const STATE_YELLOW = 'Jaune';
const STATE_ORANGE = 'Orange';
-const STATE_RED = 'Rouge';
+const STATE_RED = 'Rouge';
// The list of event alert is located here
// https://github.com/hacf-fr/meteofrance-api/blob/master/src/meteofrance_api/const.py
-const EVENT_WIND = 'Vent violent';
-const EVENT_RAIN_FLOOD = 'Pluie-inondation';
-const EVENT_THUNDERSTORMS = 'Orages';
-const EVENT_FLOOD = 'Inondation';
-const EVENT_SNOW_ICE = 'Neige-verglas';
+const EVENT_WIND = 'Vent violent';
+const EVENT_RAIN_FLOOD = 'Pluie-inondation';
+const EVENT_THUNDERSTORMS = 'Orages';
+const EVENT_FLOOD = 'Inondation';
+const EVENT_SNOW_ICE = 'Neige-verglas';
const EVENT_HIGH_TEMPERATURE = 'Canicule';
-const EVENT_LOW_TEMPERATURE = 'Grand-froid';
-const EVENT_AVALANCHES = 'Avalanches';
-const EVENT_COASTAL = 'Vagues-submersion';
+const EVENT_LOW_TEMPERATURE = 'Grand-froid';
+const EVENT_AVALANCHES = 'Avalanches';
+const EVENT_COASTAL = 'Vagues-submersion';
const EVENT_TYPES = {
- [EVENT_WIND]: MeteoalarmEventType.Wind,
- [EVENT_RAIN_FLOOD]: MeteoalarmEventType.Flooding,
- [EVENT_THUNDERSTORMS]: MeteoalarmEventType.Thunderstorms,
- [EVENT_FLOOD]: MeteoalarmEventType.Flooding,
- [EVENT_SNOW_ICE]: MeteoalarmEventType.SnowIce,
+ [EVENT_WIND]: MeteoalarmEventType.Wind,
+ [EVENT_RAIN_FLOOD]: MeteoalarmEventType.Flooding,
+ [EVENT_THUNDERSTORMS]: MeteoalarmEventType.Thunderstorms,
+ [EVENT_FLOOD]: MeteoalarmEventType.Flooding,
+ [EVENT_SNOW_ICE]: MeteoalarmEventType.SnowIce,
[EVENT_HIGH_TEMPERATURE]: MeteoalarmEventType.HighTemperature,
- [EVENT_LOW_TEMPERATURE]: MeteoalarmEventType.LowTemperature,
- [EVENT_AVALANCHES]: MeteoalarmEventType.Avalanches,
- [EVENT_COASTAL]: MeteoalarmEventType.CoastalEvent
+ [EVENT_LOW_TEMPERATURE]: MeteoalarmEventType.LowTemperature,
+ [EVENT_AVALANCHES]: MeteoalarmEventType.Avalanches,
+ [EVENT_COASTAL]: MeteoalarmEventType.CoastalEvent,
};
const LEVEL_TYPES = {
[STATE_YELLOW]: MeteoalarmLevelType.Yellow,
[STATE_ORANGE]: MeteoalarmLevelType.Orange,
- [STATE_RED]: MeteoalarmLevelType.Red
+ [STATE_RED]: MeteoalarmLevelType.Red,
};
export default class MeteoFrance implements MeteoalarmIntegration {
@@ -60,15 +60,18 @@ export default class MeteoFrance implements MeteoalarmIntegration {
MeteoalarmEventType.Flooding,
MeteoalarmEventType.SnowIce,
MeteoalarmEventType.HighTemperature,
- MeteoalarmEventType.LowTemperature,
+ MeteoalarmEventType.LowTemperature,
MeteoalarmEventType.Avalanches,
- MeteoalarmEventType.CoastalEvent
- ]
+ MeteoalarmEventType.CoastalEvent,
+ ],
};
}
public supports(entity: HassEntity): boolean {
- return entity.attributes.attribution == 'Data provided by Météo-France' && entity.attributes[EVENT_WIND] != undefined;
+ return (
+ entity.attributes.attribution == 'Data provided by Météo-France' &&
+ entity.attributes[EVENT_WIND] != undefined
+ );
}
public alertActive(entity: HassEntity): boolean {
@@ -78,13 +81,13 @@ export default class MeteoFrance implements MeteoalarmIntegration {
public getAlerts(entity: HassEntity): MeteoalarmAlert[] {
const result: MeteoalarmAlert[] = [];
- for(const [eventName, event] of Object.entries(EVENT_TYPES)) {
+ for (const [eventName, event] of Object.entries(EVENT_TYPES)) {
const eventLevel = entity.attributes[eventName];
- if(!eventLevel) continue;
- if(eventLevel === STATE_GREEN) continue;
+ if (!eventLevel) continue;
+ if (eventLevel === STATE_GREEN) continue;
result.push({
level: LEVEL_TYPES[eventLevel],
- event: event
+ event: event,
});
}
return result;
diff --git a/src/integrations/nina.ts b/src/integrations/nina.ts
index 37db358..4235009 100644
--- a/src/integrations/nina.ts
+++ b/src/integrations/nina.ts
@@ -5,16 +5,16 @@ import {
MeteoalarmIntegration,
MeteoalarmIntegrationEntityType,
MeteoalarmIntegrationMetadata,
- MeteoalarmLevelType
+ MeteoalarmLevelType,
} from '../types';
import { Utils } from '../utils';
type NinaEntity = HassEntity & {
attributes: {
- headline: string,
- severity: string,
- }
-}
+ headline: string;
+ severity: string;
+ };
+};
export default class NINA implements MeteoalarmIntegration {
public get metadata(): MeteoalarmIntegrationMetadata {
@@ -25,9 +25,7 @@ export default class NINA implements MeteoalarmIntegration {
returnHeadline: true,
returnMultipleAlerts: true,
entitiesCount: 0,
- monitoredConditions: [
- MeteoalarmEventType.Unknown
- ]
+ monitoredConditions: [MeteoalarmEventType.Unknown],
};
}
@@ -43,13 +41,14 @@ export default class NINA implements MeteoalarmIntegration {
public getAlerts(entity: NinaEntity): MeteoalarmAlert[] {
const { severity, headline } = entity.attributes;
- return [{
- event: MeteoalarmEventType.Unknown,
- headline: headline,
- level: Utils.getLevelBySeverity(
- severity, {
- 'Moderate': MeteoalarmLevelType.Orange
- })
- }];
+ return [
+ {
+ event: MeteoalarmEventType.Unknown,
+ headline: headline,
+ level: Utils.getLevelBySeverity(severity, {
+ Moderate: MeteoalarmLevelType.Orange,
+ }),
+ },
+ ];
}
}
diff --git a/src/integrations/weatheralerts.ts b/src/integrations/weatheralerts.ts
index b0fe980..d12c969 100644
--- a/src/integrations/weatheralerts.ts
+++ b/src/integrations/weatheralerts.ts
@@ -5,22 +5,22 @@ import {
MeteoalarmIntegration,
MeteoalarmIntegrationEntityType,
MeteoalarmIntegrationMetadata,
- MeteoalarmLevelType
+ MeteoalarmLevelType,
} from '../types';
import { Utils } from '../utils';
type WeatheralertsAlert = {
- event: string,
- severity: string,
- title: string
-}
+ event: string;
+ severity: string;
+ title: string;
+};
type WeatheralertsEntity = HassEntity & {
attributes: {
- integration: string,
- alerts: WeatheralertsAlert[]
- }
-}
+ integration: string;
+ alerts: WeatheralertsAlert[];
+ };
+};
export default class Weatheralerts implements MeteoalarmIntegration {
public get metadata(): MeteoalarmIntegrationMetadata {
@@ -31,7 +31,7 @@ export default class Weatheralerts implements MeteoalarmIntegration {
returnHeadline: true,
returnMultipleAlerts: true,
entitiesCount: 1,
- monitoredConditions: Utils.convertEventTypesForMetadata(this.eventTypes)
+ monitoredConditions: Utils.convertEventTypesForMetadata(this.eventTypes),
};
}
@@ -47,51 +47,51 @@ export default class Weatheralerts implements MeteoalarmIntegration {
// Event types from: https://www.weather.gov/lwx/WarningsDefined
return {
'Winter Storm': MeteoalarmEventType.SnowIce,
- 'Blizzard': MeteoalarmEventType.SnowIce,
+ Blizzard: MeteoalarmEventType.SnowIce,
'Ice Storm': MeteoalarmEventType.SnowIce,
'Winter Weather': MeteoalarmEventType.SnowIce,
- 'Freeze': MeteoalarmEventType.LowTemperature,
- 'Frost': MeteoalarmEventType.LowTemperature,
+ Freeze: MeteoalarmEventType.LowTemperature,
+ Frost: MeteoalarmEventType.LowTemperature,
'Wind Chill': MeteoalarmEventType.LowTemperature,
'Fire Weather': MeteoalarmEventType.ForestFire,
'Red Flag': MeteoalarmEventType.ForestFire,
'Dense Fog': MeteoalarmEventType.Fog,
'High Wind': MeteoalarmEventType.Wind,
- 'Wind': MeteoalarmEventType.Wind,
+ Wind: MeteoalarmEventType.Wind,
'Severe Thunderstorm': MeteoalarmEventType.Thunderstorms,
- 'Tornado': MeteoalarmEventType.Tornado,
+ Tornado: MeteoalarmEventType.Tornado,
'Extreme Wind': MeteoalarmEventType.Wind,
'Small Craft': MeteoalarmEventType.Wind,
- 'Gale': MeteoalarmEventType.SeaEvent,
- 'Storm': MeteoalarmEventType.Thunderstorms,
+ Gale: MeteoalarmEventType.SeaEvent,
+ Storm: MeteoalarmEventType.Thunderstorms,
'Hurricane Force Wind': MeteoalarmEventType.Hurricane,
'Special Marine': MeteoalarmEventType.Unknown,
'Coastal Flood': MeteoalarmEventType.Flooding,
'Flash Flood': MeteoalarmEventType.Flooding,
- 'Flood': MeteoalarmEventType.Flooding,
+ Flood: MeteoalarmEventType.Flooding,
'River Flood': MeteoalarmEventType.Flooding,
'Excessive Heat': MeteoalarmEventType.HighTemperature,
- 'Heat': MeteoalarmEventType.HighTemperature,
+ Heat: MeteoalarmEventType.HighTemperature,
'Tropical Storm': MeteoalarmEventType.Hurricane,
- 'Hurricane': MeteoalarmEventType.Hurricane,
+ Hurricane: MeteoalarmEventType.Hurricane,
'Air Quality': MeteoalarmEventType.AirQuality,
'Rip Current': MeteoalarmEventType.CoastalEvent,
'Special Weather': MeteoalarmEventType.Unknown,
'High Surf': MeteoalarmEventType.CoastalEvent,
'Hazardous Seas': MeteoalarmEventType.SeaEvent,
'Beach Hazard': MeteoalarmEventType.CoastalEvent,
- 'Blowing Dust': MeteoalarmEventType.Dust
+ 'Blowing Dust': MeteoalarmEventType.Dust,
};
}
private get eventLevels(): { [key: string]: MeteoalarmLevelType } {
// Event types from: https://www.weather.gov/lwx/WarningsDefined
return {
- 'Warning': MeteoalarmLevelType.Red,
- 'Statement': MeteoalarmLevelType.Orange,
- 'Watch': MeteoalarmLevelType.Orange,
- 'Advisory': MeteoalarmLevelType.Yellow,
- 'Alert': MeteoalarmLevelType.Yellow
+ Warning: MeteoalarmLevelType.Red,
+ Statement: MeteoalarmLevelType.Orange,
+ Watch: MeteoalarmLevelType.Orange,
+ Advisory: MeteoalarmLevelType.Yellow,
+ Alert: MeteoalarmLevelType.Yellow,
};
}
@@ -100,29 +100,29 @@ export default class Weatheralerts implements MeteoalarmIntegration {
const result: MeteoalarmAlert[] = [];
- for(const alert of alerts) {
+ for (const alert of alerts) {
const fullAlertName = alert.event;
let alertLevel: MeteoalarmLevelType | undefined = undefined;
let alertType: MeteoalarmEventType | undefined = undefined;
- for(const [levelName, level] of Object.entries(this.eventLevels)) {
- if(!fullAlertName.includes(levelName)) continue;
+ for (const [levelName, level] of Object.entries(this.eventLevels)) {
+ if (!fullAlertName.includes(levelName)) continue;
alertLevel = level;
const alertName = fullAlertName.replace(levelName, '').trim();
alertType = this.eventTypes[alertName];
- if(alertType == undefined) {
+ if (alertType == undefined) {
throw Error(`Unknown weatheralerts alert type: ${alertName}`);
}
}
- if(alertLevel == undefined) {
+ if (alertLevel == undefined) {
throw Error(`Unknown weatheralerts alert level: ${fullAlertName}`);
}
result.push({
headline: fullAlertName,
level: alertLevel,
- event: alertType!
+ event: alertType!,
});
}
return result;
diff --git a/src/localize/localize.ts b/src/localize/localize.ts
index efecda9..94401c6 100644
--- a/src/localize/localize.ts
+++ b/src/localize/localize.ts
@@ -27,42 +27,46 @@ const languages: any = {
sv: sv,
cs: cs,
pt: pt,
- bg: bg
+ bg: bg,
};
export function localize(string: string): string {
- if(string.toLocaleLowerCase() != string) {
+ if (string.toLocaleLowerCase() != string) {
// eslint-disable-next-line no-console
console.warn(`MeteoalarmCard: Received invalid translation key: ${string}`);
}
string = string.toLocaleLowerCase();
let storedLang = localStorage.getItem('selectedLanguage');
- if(storedLang === 'null') {
+ if (storedLang === 'null') {
storedLang = null;
}
- const lang = (storedLang || navigator.language.split('-')[0] || 'en').replace(/['"]+/g, '').replace('-', '_');
+ const lang = (storedLang || navigator.language.split('-')[0] || 'en')
+ .replace(/['"]+/g, '')
+ .replace('-', '_');
let translated: string | undefined = undefined;
// Try using specified language
try {
translated = string.split('.').reduce((o, i) => o[i], languages[lang]);
- }
- catch (e) {
+ } catch (e) {
// eslint-disable-next-line no-console
- console.warn(`MeteoalarmCard: Translation for "${string}" is not specified in "${lang}" language.`);
+ console.warn(
+ `MeteoalarmCard: Translation for "${string}" is not specified in "${lang}" language.`,
+ );
}
// Try using english
if (translated == undefined) {
try {
translated = string.split('.').reduce((o, i) => o[i], languages['en']);
- }
- catch (e) {
+ } catch (e) {
// eslint-disable-next-line no-console
- console.warn(`MeteoalarmCard: Translation for "${string}" is not specified in fallback english language.`);
+ console.warn(
+ `MeteoalarmCard: Translation for "${string}" is not specified in fallback english language.`,
+ );
}
}
// Fall back to string
- if(translated == undefined) translated = string;
+ if (translated == undefined) translated = string;
return translated;
}
diff --git a/src/measure-text.ts b/src/measure-text.ts
index b2ee48d..f279a23 100644
--- a/src/measure-text.ts
+++ b/src/measure-text.ts
@@ -1,11 +1,11 @@
/**
- * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
- *
- * @param {String} text The text to be rendered.
- * @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
- *
- * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
- */
+ * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
+ *
+ * @param {String} text The text to be rendered.
+ * @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
+ *
+ * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
+ */
export function getTextWidth(text: string, font: string): number {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d')!;
diff --git a/src/meteoalarm-card.ts b/src/meteoalarm-card.ts
index 6cd2e61..c3365c9 100644
--- a/src/meteoalarm-card.ts
+++ b/src/meteoalarm-card.ts
@@ -1,6 +1,13 @@
import {
- ActionHandlerEvent, debounce, EntityConfig, handleAction, hasAction, hasConfigOrEntityChanged,
- HomeAssistant, LovelaceCardConfig, LovelaceCardEditor
+ ActionHandlerEvent,
+ debounce,
+ EntityConfig,
+ handleAction,
+ hasAction,
+ hasConfigOrEntityChanged,
+ HomeAssistant,
+ LovelaceCardConfig,
+ LovelaceCardEditor,
} from 'custom-card-helpers';
import { HassEntity } from 'home-assistant-js-websocket';
import { CSSResultGroup, html, LitElement, PropertyValues, TemplateResult } from 'lit';
@@ -17,13 +24,18 @@ import INTEGRATIONS from './integrations/integrations';
import { localize } from './localize/localize';
import { getCanvasFont, getTextWidth } from './measure-text';
import styles from './styles';
-import { MeteoalarmCardConfig, MeteoalarmIntegration, MeteoalarmIntegrationEntityType, MeteoalarmScalingMode } from './types';
+import {
+ MeteoalarmCardConfig,
+ MeteoalarmIntegration,
+ MeteoalarmIntegrationEntityType,
+ MeteoalarmScalingMode,
+} from './types';
// eslint-disable-next-line no-console
console.info(
`%c MeteoalarmCard %c ${CARD_VERSION} `,
'color: white; font-weight: bold; background: #1c1c1c',
- 'color: white; font-weight: bold; background: #db4437'
+ 'color: white; font-weight: bold; background: #db4437',
);
// Push card into UI card picker
@@ -32,12 +44,11 @@ console.info(
preview: true,
type: 'meteoalarm-card',
name: localize('common.name'),
- description: localize('common.description')
+ description: localize('common.description'),
});
@customElement('meteoalarm-card')
export class MeteoalarmCard extends LitElement {
-
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private config!: MeteoalarmCardConfig;
@@ -51,7 +62,7 @@ export class MeteoalarmCard extends LitElement {
private currentEntity?: string;
static get integrations(): MeteoalarmIntegration[] {
- return INTEGRATIONS.map(i => new i());
+ return INTEGRATIONS.map((i) => new i());
}
public static async getConfigElement(): Promise {
@@ -63,53 +74,49 @@ export class MeteoalarmCard extends LitElement {
// Find fist entity that is supported by any integration
const ALLOWED_INTEGRATION_TYPES = [
MeteoalarmIntegrationEntityType.SingleEntity,
- MeteoalarmIntegrationEntityType.CurrentExpected
- ];
+ MeteoalarmIntegrationEntityType.CurrentExpected,
+ ];
- for(const entity of entities) {
+ for (const entity of entities) {
const integrations = MeteoalarmCard.integrations.filter((x) =>
- ALLOWED_INTEGRATION_TYPES.includes(x.metadata.type)
+ ALLOWED_INTEGRATION_TYPES.includes(x.metadata.type),
);
- for(const integration of integrations) {
- if(integration.supports(hass.states[entity])) {
+ for (const integration of integrations) {
+ if (integration.supports(hass.states[entity])) {
return {
entities: { entity },
- integration: integration.metadata.key
+ integration: integration.metadata.key,
};
}
}
}
return {
entities: '',
- integration: ''
+ integration: '',
};
}
public setConfig(config: LovelaceCardConfig): void {
if (!config) {
throw new Error(localize('common.invalid_configuration'));
- }
- else if (
+ } else if (
config.entities == undefined ||
(Array.isArray(config.entities) && config.entities.length == 0) ||
- (Array.isArray(config.entities) && config.entities.every(e => e == null))) {
+ (Array.isArray(config.entities) && config.entities.every((e) => e == null))
+ ) {
throw new Error(localize('error.missing_entity'));
- }
- else if (config.integration == undefined) {
+ } else if (config.integration == undefined) {
throw new Error(localize('error.invalid_integration'));
}
this.config = {
name: 'Meteoalarm',
- ...config
+ ...config,
};
}
static get styles(): CSSResultGroup {
- return [
- swiperStyles,
- styles
- ];
+ return [swiperStyles, styles];
}
public getCardSize(): number {
@@ -124,13 +131,13 @@ export class MeteoalarmCard extends LitElement {
this.measureCard();
this.attachObserver();
const swiper = (this.renderRoot as ShadowRoot).getElementById('swiper');
- if(!swiper) return;
+ if (!swiper) return;
this.swiper = new Swiper(swiper, {
modules: [Pagination],
pagination: {
- el: swiper.getElementsByClassName('swiper-pagination')[0] as HTMLElement
+ el: swiper.getElementsByClassName('swiper-pagination')[0] as HTMLElement,
},
- observer: true
+ observer: true,
});
this.swiper.on('transitionEnd', () => {
this.updateCurrentEntity();
@@ -148,9 +155,7 @@ export class MeteoalarmCard extends LitElement {
private attachObserver() {
if (!this.resizeObserver) {
- this.resizeObserver = new ResizeObserver(
- debounce(() => this.measureCard(), 250, false)
- );
+ this.resizeObserver = new ResizeObserver(debounce(() => this.measureCard(), 250, false));
}
const card = this.shadowRoot!.querySelector('ha-card');
if (!card) return;
@@ -161,27 +166,33 @@ export class MeteoalarmCard extends LitElement {
const regular = container.querySelector('.headline-regular') as HTMLElement;
const narrow = container.querySelector('.headline-narrow') as HTMLElement;
const veryNarrow = container.querySelector('.headline-verynarrow') as HTMLElement;
- return [ regular, narrow, veryNarrow ];
+ return [regular, narrow, veryNarrow];
}
private measureCard() {
if (!this.isConnected) return;
const card = this.shadowRoot!.querySelector('ha-card');
if (!card) return;
- if(this.scalingMode == MeteoalarmScalingMode.Disabled) return;
-
- const scaleHeadline = [MeteoalarmScalingMode.Scale, MeteoalarmScalingMode.HeadlineAndScale].includes(this.scalingMode);
- const swapHeadline = [MeteoalarmScalingMode.Headline, MeteoalarmScalingMode.HeadlineAndScale].includes(this.scalingMode);
+ if (this.scalingMode == MeteoalarmScalingMode.Disabled) return;
+
+ const scaleHeadline = [
+ MeteoalarmScalingMode.Scale,
+ MeteoalarmScalingMode.HeadlineAndScale,
+ ].includes(this.scalingMode);
+ const swapHeadline = [
+ MeteoalarmScalingMode.Headline,
+ MeteoalarmScalingMode.HeadlineAndScale,
+ ].includes(this.scalingMode);
const MAX_FONT_SIZE = 22;
const MIN_FONT_SIZE = 17;
// Scale headlines of each swiper card
const swiper = card.querySelector('.swiper-wrapper');
const slides = swiper?.getElementsByClassName('swiper-slide') as HTMLCollectionOf;
- for(const slide of slides) {
- const [ regular, narrow, veryNarrow ] = this.getHeadlineElements(slide);
- const sizes: [string, HTMLElement][]= [['regular', regular]];
- if(swapHeadline) {
+ for (const slide of slides) {
+ const [regular, narrow, veryNarrow] = this.getHeadlineElements(slide);
+ const sizes: [string, HTMLElement][] = [['regular', regular]];
+ if (swapHeadline) {
sizes.push(['narrow', narrow]);
sizes.push(['veryNarrow', veryNarrow]);
}
@@ -189,12 +200,15 @@ export class MeteoalarmCard extends LitElement {
this.setCardScaling(slide, 'regular', MAX_FONT_SIZE);
let isSizeSet = false;
- for(const [ size, element ] of sizes) {
- if(isSizeSet) break;
+ for (const [size, element] of sizes) {
+ if (isSizeSet) break;
const minFontSize = scaleHeadline ? MIN_FONT_SIZE : MAX_FONT_SIZE;
for (let fontSize = MAX_FONT_SIZE; fontSize >= minFontSize; fontSize--) {
- const elementSize = getTextWidth(element.textContent!, getCanvasFont(regular, fontSize + 'px'));
- if(elementSize <= regular.clientWidth) {
+ const elementSize = getTextWidth(
+ element.textContent!,
+ getCanvasFont(regular, fontSize + 'px'),
+ );
+ if (elementSize <= regular.clientWidth) {
this.setCardScaling(slide, size as any, fontSize);
isSizeSet = true;
break;
@@ -203,40 +217,39 @@ export class MeteoalarmCard extends LitElement {
}
// Fallback if measuring couldn't fit the text
- if(!isSizeSet) {
- if(swapHeadline) {
+ if (!isSizeSet) {
+ if (swapHeadline) {
this.setCardScaling(slide, 'icon', MAX_FONT_SIZE);
- }
- else {
+ } else {
this.setCardScaling(slide, 'regular' as any, MIN_FONT_SIZE);
}
-
}
}
}
- private setCardScaling(container: HTMLElement, scale: 'regular' | 'narrow' | 'veryNarrow' | 'icon', fontSize: number) {
- const [ regular, narrow, veryNarrow ] = this.getHeadlineElements(container);
+ private setCardScaling(
+ container: HTMLElement,
+ scale: 'regular' | 'narrow' | 'veryNarrow' | 'icon',
+ fontSize: number,
+ ) {
+ const [regular, narrow, veryNarrow] = this.getHeadlineElements(container);
- if(scale == 'regular') {
+ if (scale == 'regular') {
regular.style.fontSize = `${fontSize}px`;
regular.style.display = 'block';
narrow.style.display = 'none';
veryNarrow.style.display = 'none';
- }
- else if(scale == 'narrow') {
+ } else if (scale == 'narrow') {
narrow.style.fontSize = `${fontSize}px`;
regular.style.display = 'none';
narrow.style.display = 'block';
veryNarrow.style.display = 'none';
- }
- else if(scale == 'veryNarrow') {
+ } else if (scale == 'veryNarrow') {
veryNarrow.style.fontSize = `${fontSize}px`;
regular.style.display = 'none';
narrow.style.display = 'none';
veryNarrow.style.display = 'block';
- }
- else if(scale == 'icon') {
+ } else if (scale == 'icon') {
regular.style.display = 'none';
narrow.style.display = 'none';
veryNarrow.style.display = 'none';
@@ -245,12 +258,14 @@ export class MeteoalarmCard extends LitElement {
private get entities(): HassEntity[] {
const entities: EntityConfig[] = processConfigEntities(this.config.entities!);
- return entities.map(e => this.hass.states[e.entity]);
+ return entities.map((e) => this.hass.states[e.entity]);
}
private get integration(): MeteoalarmIntegration {
- const integration = MeteoalarmCard.integrations.find(i => i.metadata.key === this.config.integration);
- if(integration === undefined) {
+ const integration = MeteoalarmCard.integrations.find(
+ (i) => i.metadata.key === this.config.integration,
+ );
+ if (integration === undefined) {
throw new Error(localize('error.invalid_integration'));
}
return integration!;
@@ -258,8 +273,8 @@ export class MeteoalarmCard extends LitElement {
private get scalingMode(): MeteoalarmScalingMode {
const modeString = this.config.scaling_mode;
- if(!modeString) return MeteoalarmScalingMode.HeadlineAndScale;
- if(!(Object.values(MeteoalarmScalingMode)).includes(modeString as any)) {
+ if (!modeString) return MeteoalarmScalingMode.HeadlineAndScale;
+ if (!Object.values(MeteoalarmScalingMode).includes(modeString as any)) {
throw new Error('MeteoalarmCard: ' + localize('error.invalid_scaling_mode'));
}
return modeString as MeteoalarmScalingMode;
@@ -274,13 +289,15 @@ export class MeteoalarmCard extends LitElement {
this.config.override_headline,
this.config.hide_caption,
this.config.ignored_levels,
- this.config.ignored_events
+ this.config.ignored_events,
);
// Handle hide_when_no_warning
- if(events.every(e => !e.isActive) && this.config.hide_when_no_warning) {
+ if (events.every((e) => !e.isActive) && this.config.hide_when_no_warning) {
// eslint-disable-next-line no-console
- console.log('MeteoalarmCard: Card is hidden - hide_when_no_warning is enabled and there are no warnings');
+ console.log(
+ 'MeteoalarmCard: Card is hidden - hide_when_no_warning is enabled and there are no warnings',
+ );
this.setCardMargin(false);
return html``;
}
@@ -292,38 +309,45 @@ export class MeteoalarmCard extends LitElement {
@action=${this.handleAction}
.actionHandler=${actionHandler({
hasHold: hasAction(this.config.hold_action),
- hasDoubleClick: hasAction(this.config.double_tap_action)
+ hasDoubleClick: hasAction(this.config.double_tap_action),
})}
tabindex="0"
>
-
+
- ${events.map((event => html`
-
-
- ${this.renderMainIcon(event.icon)}
- ${this.renderHeadlines(event.headlines)}
-
- ${event.caption && event.captionIcon ? html`
-
- ${this.renderCaption(event.captionIcon, event.caption)}
+ ${events.map(
+ (event) => html`
+
+
+ ${this.renderMainIcon(event.icon)} ${this.renderHeadlines(event.headlines)}
- ` : ''}
-
- `))}
+ ${event.caption && event.captionIcon
+ ? html`
+
+ ${this.renderCaption(event.captionIcon, event.caption)}
+
+ `
+ : ''}
+
+ `,
+ )}
`;
- }
- catch(error) {
+ } catch (error) {
// eslint-disable-next-line no-console
console.error('[METEOALARM CARD ERROR]\nReport issue: https://bit.ly/3hK1hL4 \n\n', error);
return this.showError(error as string);
@@ -331,7 +355,10 @@ export class MeteoalarmCard extends LitElement {
}
private renderMainIcon(icon: string): TemplateResult {
- return html`
`;
+ return html`
`;
}
// Transfer array of one, two or three headlines in descending length
@@ -339,26 +366,24 @@ export class MeteoalarmCard extends LitElement {
// card width (screen size) by resize observer
private renderHeadlines(headlines: string[]): TemplateResult {
// TODO: Fix this array mess
- let regular = '', narrow = '', verynarrow = '';
- if(headlines.length == 0) {
+ let regular = '',
+ narrow = '',
+ verynarrow = '';
+ if (headlines.length == 0) {
throw new Error('headlines array length is 0');
- }
- else if(headlines.length == 1) {
+ } else if (headlines.length == 1) {
regular = headlines[0];
narrow = headlines[0];
verynarrow = headlines[0];
- }
- else if(headlines.length == 2) {
+ } else if (headlines.length == 2) {
regular = headlines[0];
narrow = headlines[1];
verynarrow = headlines[1];
- }
- else if(headlines.length == 3) {
+ } else if (headlines.length == 3) {
regular = headlines[0];
narrow = headlines[1];
verynarrow = headlines[2];
- }
- else if(headlines.length > 3) {
+ } else if (headlines.length > 3) {
throw new Error('headlines array length is higher than 3');
}
@@ -372,13 +397,16 @@ export class MeteoalarmCard extends LitElement {
private renderCaption(icon: string, caption: string): TemplateResult {
return html`
${caption}
-
+
`;
}
private setCardMargin(showMargin: boolean): void {
const container = this.shadowRoot?.host as HTMLElement;
- if(!container) return;
+ if (!container) return;
container.style.margin = showMargin ? '' : '0px';
}
@@ -387,7 +415,7 @@ export class MeteoalarmCard extends LitElement {
errorCard.setConfig({
type: 'error',
error,
- origConfig: this.config
+ origConfig: this.config,
});
return html` ${errorCard} `;
@@ -396,7 +424,7 @@ export class MeteoalarmCard extends LitElement {
private handleAction(ev: ActionHandlerEvent): void {
const config = {
...this.config,
- entity: this.currentEntity
+ entity: this.currentEntity,
};
if (this.hass && this.config && ev.detail.action) {
handleAction(this, this.hass, config, ev.detail.action);
diff --git a/src/predefined-cards.ts b/src/predefined-cards.ts
index ebbbfc4..d8ebcb9 100644
--- a/src/predefined-cards.ts
+++ b/src/predefined-cards.ts
@@ -10,10 +10,7 @@ export class PredefinedCards {
entity: undefined,
icon: 'cloud-question',
color: MeteoalarmData.getLevel(MeteoalarmLevelType.None).color,
- headlines: [
- localize('common.unavailable.long'),
- localize('common.unavailable.short')
- ]
+ headlines: [localize('common.unavailable.long'), localize('common.unavailable.short')],
};
}
@@ -23,10 +20,7 @@ export class PredefinedCards {
entity: entity,
icon: 'shield-outline',
color: MeteoalarmData.getLevel(MeteoalarmLevelType.None).color,
- headlines: [
- localize('events.no_warnings')
- ]
+ headlines: [localize('events.no_warnings')],
};
}
}
-
diff --git a/src/styles.ts b/src/styles.ts
index 4123ce1..39abb17 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -1,101 +1,94 @@
import { css } from 'lit';
export default css`
- :host
- {
- display: flex;
- flex: 1;
- flex-direction: column;
- }
+ :host {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ }
- ha-card
- {
- flex-direction: column;
- flex: 1;
- position: relative;
- padding: 0px;
- border-radius: var(--ha-card-border-radius, 12px);
- box-shadow: var(
- --ha-card-box-shadow,
- 0px 2px 1px -1px rgba(0, 0, 0, 0.2),
- 0px 1px 1px 0px rgba(0, 0, 0, 0.14),
- 0px 1px 3px 0px rgba(0, 0, 0, 0.12));
- overflow: hidden;
- transition: all 0.3s ease-out 0s;
- }
+ ha-card {
+ flex-direction: column;
+ flex: 1;
+ position: relative;
+ padding: 0px;
+ border-radius: var(--ha-card-border-radius, 12px);
+ box-shadow: var(
+ --ha-card-box-shadow,
+ 0px 2px 1px -1px rgba(0, 0, 0, 0.2),
+ 0px 1px 1px 0px rgba(0, 0, 0, 0.14),
+ 0px 1px 3px 0px rgba(0, 0, 0, 0.12)
+ );
+ overflow: hidden;
+ transition: all 0.3s ease-out 0s;
+ }
- a
- {
- color: var(--secondary-text-color);
- }
+ a {
+ color: var(--secondary-text-color);
+ }
- .container
- {
- background: var(--card-background-color);
- cursor: pointer;
- overflow: hidden;
- position: relative;
- }
+ .container {
+ background: var(--card-background-color);
+ cursor: pointer;
+ overflow: hidden;
+ position: relative;
+ }
- .content
- {
- display: flex;
- padding: 36px 28px;
- justify-content: center;
- }
+ .content {
+ display: flex;
+ padding: 36px 28px;
+ justify-content: center;
+ }
- .main-icon
- {
- --mdc-icon-size: 50px;
- height: 50px;
- flex: 0;
- }
+ .main-icon {
+ --mdc-icon-size: 50px;
+ height: 50px;
+ flex: 0;
+ }
- .headline
- {
- flex: 1;
- font-size: 22px;
- line-height: normal;
- margin: auto;
- margin-left: 18px;
- text-align: center;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
+ .headline {
+ flex: 1;
+ font-size: 22px;
+ line-height: normal;
+ margin: auto;
+ margin-left: 18px;
+ text-align: center;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ }
- .caption {
- top: 0;
- right: 0;
- position: absolute;
- display: flex;
- align-items: center;
- margin: 10px 12px;
- font-size: 13px;
- line-height: normal;
- }
+ .caption {
+ top: 0;
+ right: 0;
+ position: absolute;
+ display: flex;
+ align-items: center;
+ margin: 10px 12px;
+ font-size: 13px;
+ line-height: normal;
+ }
- .caption-icon
- {
- --mdc-icon-size: 19px;
- height: 19px;
- flex: 0;
- margin-left: 5px;
- }
+ .caption-icon {
+ --mdc-icon-size: 19px;
+ height: 19px;
+ flex: 0;
+ margin-left: 5px;
+ }
- .headline-narrow, .headline-verynarrow
- {
- display: none;
- }
+ .headline-narrow,
+ .headline-verynarrow {
+ display: none;
+ }
- .swiper {
- --swiper-pagination-bullet-size: 5px;
- }
+ .swiper {
+ --swiper-pagination-bullet-size: 5px;
+ }
- .swiper-pagination-bullet {
- background-color: #dfdfdf;
- }
- .swiper-pagination-bullet-active {
- background-color: #ffffff;
- }
+ .swiper-pagination-bullet {
+ background-color: #dfdfdf;
+ }
+ .swiper-pagination-bullet-active {
+ background-color: #ffffff;
+ }
`;
diff --git a/src/types.ts b/src/types.ts
index 7df5d93..02d2c35 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,131 +1,136 @@
-import { ActionConfig, EntityConfig, LovelaceCard, LovelaceCardConfig, LovelaceCardEditor } from 'custom-card-helpers';
+import {
+ ActionConfig,
+ EntityConfig,
+ LovelaceCard,
+ LovelaceCardConfig,
+ LovelaceCardEditor,
+} from 'custom-card-helpers';
import { HassEntity } from 'home-assistant-js-websocket';
declare global {
- interface HTMLElementTagNameMap {
- 'meteoalarm-card-editor': LovelaceCardEditor;
- 'hui-error-card': LovelaceCard;
- }
+ interface HTMLElementTagNameMap {
+ 'meteoalarm-card-editor': LovelaceCardEditor;
+ 'hui-error-card': LovelaceCard;
+ }
}
export interface MeteoalarmCardConfig extends LovelaceCardConfig {
- type: string;
- entities?: string | string[] | EntityConfig[];
- integration?: string;
- override_headline?: boolean;
- hide_when_no_warning?: boolean;
- hide_caption?: boolean;
- disable_swiper?: boolean;
- scaling_mode?: string;
- ignored_events?: string[];
- ignored_levels?: string[];
+ type: string;
+ entities?: string | string[] | EntityConfig[];
+ integration?: string;
+ override_headline?: boolean;
+ hide_when_no_warning?: boolean;
+ hide_caption?: boolean;
+ disable_swiper?: boolean;
+ scaling_mode?: string;
+ ignored_events?: string[];
+ ignored_levels?: string[];
- tap_action?: ActionConfig;
- hold_action?: ActionConfig;
- double_tap_action?: ActionConfig;
+ tap_action?: ActionConfig;
+ hold_action?: ActionConfig;
+ double_tap_action?: ActionConfig;
}
export interface MeteoalarmIntegration {
- metadata: MeteoalarmIntegrationMetadata,
- supports(entity: HassEntity): boolean,
- alertActive(entity: HassEntity): boolean,
- getAlerts(entity: HassEntity): MeteoalarmAlert[] | MeteoalarmAlert,
+ metadata: MeteoalarmIntegrationMetadata;
+ supports(entity: HassEntity): boolean;
+ alertActive(entity: HassEntity): boolean;
+ getAlerts(entity: HassEntity): MeteoalarmAlert[] | MeteoalarmAlert;
}
export interface MeteoalarmIntegrationMetadata {
- key: string,
- name: string,
- type: MeteoalarmIntegrationEntityType,
- entitiesCount: number
- returnHeadline: boolean,
- returnMultipleAlerts: boolean,
- monitoredConditions: MeteoalarmEventType[]
+ key: string;
+ name: string;
+ type: MeteoalarmIntegrationEntityType;
+ entitiesCount: number;
+ returnHeadline: boolean;
+ returnMultipleAlerts: boolean;
+ monitoredConditions: MeteoalarmEventType[];
}
export enum MeteoalarmIntegrationEntityType {
- // Alerts in this integrations all all in attributes of single entity
- SingleEntity = 0,
- // Alerts in this integration are split across two entities
- // one contains current warnings and another future warnings
- CurrentExpected = 1,
- // Alerts in this integration are split across multiple (probably unlimited amount) of entities
- // each one contains one warning
- Slots = 2,
- // Alerts in this integration are split across exactly 4 entities: warnings, watches, statements, advisories
- WarningWatchStatementAdvisory = 3,
- // Alerts in this integration are split across multiple entities, count is strictly specified
- // Each warning is dedicated for one entity kind
- SeparateEvents = 4
+ // Alerts in this integrations all all in attributes of single entity
+ SingleEntity = 0,
+ // Alerts in this integration are split across two entities
+ // one contains current warnings and another future warnings
+ CurrentExpected = 1,
+ // Alerts in this integration are split across multiple (probably unlimited amount) of entities
+ // each one contains one warning
+ Slots = 2,
+ // Alerts in this integration are split across exactly 4 entities: warnings, watches, statements, advisories
+ WarningWatchStatementAdvisory = 3,
+ // Alerts in this integration are split across multiple entities, count is strictly specified
+ // Each warning is dedicated for one entity kind
+ SeparateEvents = 4,
}
/**
* Is the alert currently active or will be active in the future
* This is mostly used with type MeteoalarmIntegrationEntityType.CurrentExpected
*/
export enum MeteoalarmAlertKind {
-
- Current = 0,
- Expected = 1
+ Current = 0,
+ Expected = 1,
}
export enum MeteoalarmScalingMode {
- Disabled = 'disabled',
- Headline = 'headline',
- Scale = 'scale',
- HeadlineAndScale = 'headline_and_scale'
+ Disabled = 'disabled',
+ Headline = 'headline',
+ Scale = 'scale',
+ HeadlineAndScale = 'headline_and_scale',
}
// Event returned by the integration
export interface MeteoalarmAlert {
- event: MeteoalarmEventType,
- level: MeteoalarmLevelType,
- headline?: string,
- kind?: MeteoalarmAlertKind,
- _entity?: HassEntity
+ event: MeteoalarmEventType;
+ level: MeteoalarmLevelType;
+ headline?: string;
+ kind?: MeteoalarmAlertKind;
+ _entity?: HassEntity;
}
/**
* Event transformed from MeteoalarmEvent used for rendering card
*/
export interface MeteoalarmAlertParsed {
- /**
- * Should this alert be shown when hide_when_no_warnings is enabled
- */
- isActive: boolean,
- icon: string,
- color: string,
- headlines: string[],
- captionIcon?: string,
- caption?: string,
- entity?: HassEntity,
+ /**
+ * Should this alert be shown when hide_when_no_warnings is enabled
+ */
+ isActive: boolean;
+ icon: string;
+ color: string;
+ headlines: string[];
+ captionIcon?: string;
+ caption?: string;
+ entity?: HassEntity;
}
export enum MeteoalarmEventType {
- Unknown,
- Nuclear,
- Hurricane,
- Tornado,
- CoastalEvent,
- ForestFire,
- Avalanches,
- Earthquake,
- Volcano,
- Flooding,
- SeaEvent,
- Thunderstorms,
- Rain,
- SnowIce,
- HighTemperature,
- LowTemperature,
- Wind,
- Fog,
- AirQuality,
- Dust,
- Tsunami
+ Unknown,
+ Nuclear,
+ Hurricane,
+ Tornado,
+ CoastalEvent,
+ ForestFire,
+ Avalanches,
+ Earthquake,
+ Volcano,
+ Flooding,
+ SeaEvent,
+ Thunderstorms,
+ Rain,
+ SnowIce,
+ HighTemperature,
+ LowTemperature,
+ Wind,
+ Fog,
+ AirQuality,
+ Dust,
+ Tsunami,
}
export enum MeteoalarmLevelType {
- Red = 3,
- Orange = 2,
- Yellow = 1,
- None = 0
+ Red = 3,
+ Orange = 2,
+ Yellow = 1,
+ None = 0,
}
diff --git a/src/utils.ts b/src/utils.ts
index d4141b6..fe17fef 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -7,10 +7,10 @@ export class Utils {
*/
public static minHAversion(minYear: number, minMonth: number): boolean {
const rawVersion = (window as any).frontendVersion as string;
- if(!rawVersion) return false;
- const year = rawVersion.substring(0,4);
- const version = rawVersion.substring(4,6);
- return Number(year) >= minYear || (Number(year) >= minYear && Number(version) >= minMonth) ;
+ if (!rawVersion) return false;
+ const year = rawVersion.substring(0, 4);
+ const version = rawVersion.substring(4, 6);
+ return Number(year) >= minYear || (Number(year) >= minYear && Number(version) >= minMonth);
}
/**
@@ -27,11 +27,14 @@ export class Utils {
* For example `{ "Moderate": MeteoalarmLevelType.Orange }`
* @returns
*/
- public static getLevelBySeverity(severity: string, overrides?: { [key: string]: MeteoalarmLevelType }): MeteoalarmLevelType {
- if(overrides && overrides[severity]) {
+ public static getLevelBySeverity(
+ severity: string,
+ overrides?: { [key: string]: MeteoalarmLevelType },
+ ): MeteoalarmLevelType {
+ if (overrides && overrides[severity]) {
return overrides[severity];
}
- switch(severity) {
+ switch (severity) {
case 'Unknown':
case 'Minor':
case 'Moderate':
@@ -51,7 +54,9 @@ export class Utils {
* function convert this list for metadata.monitoredConditions
* @param eventTypes eventTypes dict
*/
- public static convertEventTypesForMetadata(eventTypes: { [key: number | string]: MeteoalarmEventType }): MeteoalarmEventType[] {
+ public static convertEventTypesForMetadata(eventTypes: {
+ [key: number | string]: MeteoalarmEventType;
+ }): MeteoalarmEventType[] {
return [...new Set(Object.values(eventTypes))];
}
}