Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example support for Burze.dzis.net v2.0.0 #203

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions src/events-praser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
MeteoalarmIntegration,
MeteoalarmIntegrationEntityType
} from './types';
import { HomeAssistant } from 'custom-card-helpers';

/**
* This is the class that stands between integration and rendering code.
Expand All @@ -24,20 +25,21 @@ class EventsParser {
* to integration specified in constructors. The result is additionally processed and
* verified then, packed to array of MeteoalarmAlertParsed objects
*/
public getEvents(
public async getEvents(
hass: HomeAssistant,
entities: HassEntity[],
disableSweeper = false,
overrideHeadline = false,
hideCaption = false,
ignoredLevels: string[] = [],
ignoredEvents: string[] = []
): MeteoalarmAlertParsed[] {
): Promise<MeteoalarmAlertParsed[]> {
if(this.isAnyEntityUnavailable(entities)) {
return [ PredefinedCards.unavailableCard() ];
}
this.checkIfIntegrationSupportsEntities(entities);
await this.checkIfIntegrationSupportsEntities(hass, entities);

let alerts = this.sortAlerts(this.graterAllAlerts(entities));
let alerts = this.sortAlerts(await this.graterAllAlerts(hass, entities));
this.validateAlert(alerts);
alerts = this.filterAlerts(alerts, ignoredLevels, ignoredEvents);

Expand Down Expand Up @@ -85,13 +87,13 @@ class EventsParser {
/**
* Call integration for each of the entities and put all alerts in array
*/
private graterAllAlerts(entities: HassEntity[]): MeteoalarmAlert[] {
private async graterAllAlerts(hass: HomeAssistant, entities: HassEntity[]): Promise<MeteoalarmAlert[]> {
const alerts: MeteoalarmAlert[] = [];
for(const entity of entities) {
const active = this.integration.alertActive(entity);
if(!active) continue;

let entityAlerts = this.integration.getAlerts(entity);
let entityAlerts = await this.integration.getAlerts(hass, entity);
if(!Array.isArray(entityAlerts)) {
entityAlerts = [ entityAlerts ];
}
Expand Down Expand Up @@ -204,16 +206,20 @@ class EventsParser {
});
}

private checkIfIntegrationSupportsEntities(entities: HassEntity[]): void {
if(!entities.every(e => this.integration.supports(e))) {
private async checkIfIntegrationSupportsEntities(hass: HomeAssistant, entities: HassEntity[]): Promise<void> {
const supportData = await Promise.all(entities.map(async e => {
const supports = await this.integration.supports(hass, e);
return {entity: e, supports};
}));
if(!supportData.every(e => e.supports)) {
if(entities.length == 1) {
throw new Error(localize('error.entity_invalid.single'));
}
else {
const unsupportedEntities = entities.filter(e => !this.integration.supports(e));
const unsupportedEntities = supportData.filter(e => !e.supports);
throw new Error(
localize('error.entity_invalid.multiple')
.replace('{entity}', unsupportedEntities.map(x => x.entity_id).join(', '))
.replace('{entity}', unsupportedEntities.map(x => x.entity.entity_id).join(', '))
);
}
}
Expand Down
26 changes: 15 additions & 11 deletions src/integrations/burze_dzis_net.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
MeteoalarmIntegrationEntityType,
MeteoalarmIntegrationMetadata
} from '../types';
import { HomeAssistant } from 'custom-card-helpers';
import { Utils } from '../utils';

type BurzeDzisNetEntity = HassEntity & {
attributes: {
Expand Down Expand Up @@ -35,42 +37,44 @@ export default class BurzeDzisNet implements MeteoalarmIntegration {
};
}

public supports(entity: BurzeDzisNetEntity): boolean {
public async supports(hass: HomeAssistant, entity: BurzeDzisNetEntity): Promise<boolean> {
return (
entity.attributes.attribution == 'Information provided by Burze.dzis.net.' &&
this.getEventType(entity) !== undefined);
await this.getEventType(hass, entity) !== undefined);
}

public alertActive(entity: BurzeDzisNetEntity): boolean {
return entity.state === 'on';
}

public getAlerts(entity: BurzeDzisNetEntity): MeteoalarmAlert {
const event = this.getEventType(entity)!;
public async getAlerts(hass: HomeAssistant, entity: BurzeDzisNetEntity): Promise<MeteoalarmAlert> {
const event = (await this.getEventType(hass, entity))!;
return {
event: event,
level: entity.attributes.level!,
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')) {
private async getEventType(hass: HomeAssistant, entity: HassEntity): Promise<MeteoalarmEventType | undefined> {
const entityInfo = await Utils.getEntityInfo(hass, entity);

if(entityInfo.unique_id.match('binary_sensor_warning_(present|active)_frost_warning')) {
return MeteoalarmEventType.LowTemperature;
}
else if(entity.entity_id.endsWith('heat_warning') && entity.attributes.friendly_name?.endsWith('Ostrzeżenie - Upał')) {
else if(entityInfo.unique_id.match('binary_sensor_warning_(present|active)_heat_warning')) {
return MeteoalarmEventType.HighTemperature;
}
else if(entity.entity_id.endsWith('precipitation_warning') && entity.attributes.friendly_name?.endsWith('Ostrzeżenie - Opad')) {
else if(entityInfo.unique_id.match('binary_sensor_warning_(present|active)_precipitation_warning')) {
return MeteoalarmEventType.Rain;
}
else if(entity.entity_id.endsWith('storm_warning') && entity.attributes.friendly_name?.endsWith('Ostrzeżenie - Burza')) {
else if(entityInfo.unique_id.match('binary_sensor_warning_(present|active)_storm_warning')) {
return MeteoalarmEventType.Thunderstorms;
}
else if(entity.entity_id.endsWith('tornado_warning') && entity.attributes.friendly_name?.endsWith('Ostrzeżenie - Trąba')) {
else if(entityInfo.unique_id.match('binary_sensor_warning_(present|active)_tornado_warning')) {
return MeteoalarmEventType.Tornado;
}
else if(entity.entity_id.endsWith('wind_warning') && entity.attributes.friendly_name?.endsWith(' Ostrzeżenie - Wiatr')) {
else if(entityInfo.unique_id.match('binary_sensor_warning_(present|active)_wind_warning')) {
return MeteoalarmEventType.Wind;
}
return undefined;
Expand Down
5 changes: 3 additions & 2 deletions src/integrations/dwd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
MeteoalarmLevelType
} from '../types';
import { Utils } from '../utils';
import { HomeAssistant } from 'custom-card-helpers';

type DWDEntity = HassEntity & {
attributes: {
Expand All @@ -30,7 +31,7 @@ export default class DWD implements MeteoalarmIntegration {
};
}

public supports(entity: DWDEntity): boolean {
public async supports(_hass: HomeAssistant, entity: DWDEntity): Promise<boolean> {
return entity.attributes.attribution == 'Data provided by DWD' && this.getEntityKind(entity) !== undefined;
}

Expand Down Expand Up @@ -105,7 +106,7 @@ export default class DWD implements MeteoalarmIntegration {
};
}

public getAlerts(entity: HassEntity): MeteoalarmAlert[] {
public async getAlerts(_hass: HomeAssistant, entity: HassEntity): Promise<MeteoalarmAlert[]> {
const { warning_count: warningCount } = entity.attributes;

const result: MeteoalarmAlert[] = [];
Expand Down
5 changes: 3 additions & 2 deletions src/integrations/env_canada.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
MeteoalarmIntegrationMetadata,
MeteoalarmLevelType
} from '../types';
import { HomeAssistant } from 'custom-card-helpers';

type EnvCanadaEntity = HassEntity & {
attributes: {
Expand Down Expand Up @@ -37,7 +38,7 @@ export default class EnvironmentCanada implements MeteoalarmIntegration {
};
}

public supports(entity: EnvCanadaEntity): boolean {
public async supports(_hass: HomeAssistant, entity: EnvCanadaEntity): Promise<boolean> {
const isStateNumber = !Number.isNaN(Number(entity.state));
return (
[ATTRIBUTION_EN, ATTRIBUTION_FR].includes(entity.attributes.attribution) &&
Expand Down Expand Up @@ -231,7 +232,7 @@ export default class EnvironmentCanada implements MeteoalarmIntegration {
});
}

public getAlerts(entity: EnvCanadaEntity): MeteoalarmAlert[] {
public async getAlerts(_hass: HomeAssistant, entity: EnvCanadaEntity): Promise<MeteoalarmAlert[]> {
const warningCount = Number(entity.state);

const result: MeteoalarmAlert[] = [];
Expand Down
5 changes: 3 additions & 2 deletions src/integrations/meteoalarm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
MeteoalarmLevelType
} from '../types';
import { Utils } from '../utils';
import { HomeAssistant } from 'custom-card-helpers';

type MeteoalarmEntity = HassEntity & {
attributes: {
Expand Down Expand Up @@ -39,7 +40,7 @@ export default class Meteoalarm implements MeteoalarmIntegration {
};
}

public supports(entity: MeteoalarmEntity): boolean {
public async supports(_hass: HomeAssistant, entity: MeteoalarmEntity): Promise<boolean> {
return entity.attributes.attribution == 'Information provided by MeteoAlarm';
}

Expand All @@ -65,7 +66,7 @@ export default class Meteoalarm implements MeteoalarmIntegration {
];
}

public getAlerts(entity: MeteoalarmEntity): MeteoalarmAlert[] {
public async getAlerts(_hass: HomeAssistant, entity: MeteoalarmEntity): Promise<MeteoalarmAlert[]> {
const {
event: eventHeadline,
headline,
Expand Down
5 changes: 3 additions & 2 deletions src/integrations/meteofrance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
MeteoalarmIntegrationMetadata,
MeteoalarmLevelType
} from '../types';
import { HomeAssistant } from 'custom-card-helpers';

const STATE_GREEN = 'Vert';
const STATE_YELLOW = 'Jaune';
Expand Down Expand Up @@ -67,15 +68,15 @@ export default class MeteoFrance implements MeteoalarmIntegration {
};
}

public supports(entity: HassEntity): boolean {
public async supports(_hass: HomeAssistant, entity: HassEntity): Promise<boolean> {
return entity.attributes.attribution == 'Data provided by Météo-France' && entity.attributes[EVENT_WIND] != undefined;
}

public alertActive(entity: HassEntity): boolean {
return entity.state !== STATE_GREEN;
}

public getAlerts(entity: HassEntity): MeteoalarmAlert[] {
public async getAlerts(_hass: HomeAssistant, entity: HassEntity): Promise<MeteoalarmAlert[]> {
const result: MeteoalarmAlert[] = [];

for(const [eventName, event] of Object.entries(EVENT_TYPES)) {
Expand Down
5 changes: 3 additions & 2 deletions src/integrations/nina.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
MeteoalarmLevelType
} from '../types';
import { Utils } from '../utils';
import { HomeAssistant } from 'custom-card-helpers';

type NinaEntity = HassEntity & {
attributes: {
Expand All @@ -31,7 +32,7 @@ export default class NINA implements MeteoalarmIntegration {
};
}

public supports(entity: NinaEntity): boolean {
public async supports(_hass: HomeAssistant, entity: NinaEntity): Promise<boolean> {
// Nina doesn't really provide a good way of verification
return ['on', 'off'].includes(entity.state);
}
Expand All @@ -40,7 +41,7 @@ export default class NINA implements MeteoalarmIntegration {
return entity.state == 'on';
}

public getAlerts(entity: NinaEntity): MeteoalarmAlert[] {
public async getAlerts(_hass: HomeAssistant, entity: NinaEntity): Promise<MeteoalarmAlert[]> {
const { severity, headline } = entity.attributes;

return [{
Expand Down
5 changes: 3 additions & 2 deletions src/integrations/weatheralerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
MeteoalarmLevelType
} from '../types';
import { Utils } from '../utils';
import { HomeAssistant } from 'custom-card-helpers';

type WeatheralertsAlert = {
event: string,
Expand Down Expand Up @@ -35,7 +36,7 @@ export default class Weatheralerts implements MeteoalarmIntegration {
};
}

public supports(entity: WeatheralertsEntity): boolean {
public async supports(_hass: HomeAssistant, entity: WeatheralertsEntity): Promise<boolean> {
return entity.attributes.integration == 'weatheralerts';
}

Expand Down Expand Up @@ -95,7 +96,7 @@ export default class Weatheralerts implements MeteoalarmIntegration {
};
}

public getAlerts(entity: WeatheralertsEntity): MeteoalarmAlert[] {
public async getAlerts(_hass: HomeAssistant, entity: WeatheralertsEntity): Promise<MeteoalarmAlert[]> {
const { alerts } = entity.attributes;

const result: MeteoalarmAlert[] = [];
Expand Down
40 changes: 28 additions & 12 deletions src/meteoalarm-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ 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 {
MeteoalarmAlertParsed,
MeteoalarmCardConfig,
MeteoalarmIntegration,
MeteoalarmIntegrationEntityType,
MeteoalarmScalingMode
} from './types';

// eslint-disable-next-line no-console
console.info(
Expand Down Expand Up @@ -49,6 +55,7 @@ export class MeteoalarmCard extends LitElement {
// Entity of which alert is displayed on currently selected slide
// Used to display correct entity on click
private currentEntity?: string;
private events: MeteoalarmAlertParsed[] | undefined = undefined;

static get integrations(): MeteoalarmIntegration[] {
return INTEGRATIONS.map(i => new i());
Expand All @@ -59,7 +66,7 @@ export class MeteoalarmCard extends LitElement {
return document.createElement('meteoalarm-card-editor');
}

public static getStubConfig(hass: HomeAssistant, entities: string[]): Record<string, unknown> {
public static async getStubConfig(hass: HomeAssistant, entities: string[]): Promise<Record<string, unknown>> {
// Find fist entity that is supported by any integration
const ALLOWED_INTEGRATION_TYPES = [
MeteoalarmIntegrationEntityType.SingleEntity,
Expand All @@ -71,7 +78,7 @@ export class MeteoalarmCard extends LitElement {
ALLOWED_INTEGRATION_TYPES.includes(x.metadata.type)
);
for(const integration of integrations) {
if(integration.supports(hass.states[entity])) {
if(await integration.supports(hass, hass.states[entity])) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For code style, move all of these to be such as such:

const supports = await integration.supports(hass, hass.states[entity];
if(supports) {
	...
}

return {
entities: { entity },
integration: integration.metadata.key
Expand Down Expand Up @@ -265,17 +272,26 @@ export class MeteoalarmCard extends LitElement {
return modeString as MeteoalarmScalingMode;
}

protected override async scheduleUpdate(): Promise<void> {
const parser = new EventsParser(this.integration);
this.events = await parser.getEvents(
this.hass,
this.entities,
this.config.disable_swiper,
this.config.override_headline,
this.config.hide_caption,
this.config.ignored_levels,
this.config.ignored_events
);
await super.scheduleUpdate();
}

protected render(): TemplateResult | void {
try {
const parser = new EventsParser(this.integration);
const events = parser.getEvents(
this.entities,
this.config.disable_swiper,
this.config.override_headline,
this.config.hide_caption,
this.config.ignored_levels,
this.config.ignored_events
);
const events = this.events;
if(!events) {
return;
}

// Handle hide_when_no_warning
if(events.every(e => !e.isActive) && this.config.hide_when_no_warning) {
Expand Down
Loading