meteoalarm-card
Version:
Meteoalarm card for Home Assistant Lovelace UI
303 lines (283 loc) • 8.39 kB
text/typescript
import { HassEntity } from 'home-assistant-js-websocket';
import {
MeteoalarmAlert,
MeteoalarmEventType,
MeteoalarmIntegration,
MeteoalarmIntegrationEntityType,
MeteoalarmIntegrationMetadata,
MeteoalarmLevelType,
} from '../types';
type EnvCanadaEntity = HassEntity & {
attributes: {
attribution: string;
};
};
export enum EnvCanadaEntityType {
Warning,
Watch,
Statement,
Advisory,
}
const ATTRIBUTION_EN = 'Data provided by Environment Canada';
const ATTRIBUTION_FR = 'Données fournies par Environnement Canada';
export default class EnvironmentCanada implements MeteoalarmIntegration {
public get metadata(): MeteoalarmIntegrationMetadata {
return {
key: 'env_canada',
name: 'Environment Canada',
type: MeteoalarmIntegrationEntityType.WarningWatchStatementAdvisory,
returnHeadline: false,
returnMultipleAlerts: true,
entitiesCount: 4,
monitoredConditions: [...new Set(this.eventTypes.map((e) => e.type))],
};
}
public supports(entity: EnvCanadaEntity): boolean {
const isStateNumber = !Number.isNaN(Number(entity.state));
return (
[ATTRIBUTION_EN, ATTRIBUTION_FR].includes(entity.attributes.attribution) &&
this.getEntityType(entity) !== undefined &&
isStateNumber
);
}
public alertActive(entity: EnvCanadaEntity): boolean {
return Number(entity.state) > 0;
}
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,
},
{
en: 'Blizzard',
fr: 'Blizzard',
type: MeteoalarmEventType.SnowIce,
},
{
en: 'Blowing Snow',
fr: 'Poudrerie',
type: MeteoalarmEventType.SnowIce,
},
{
en: 'Dust Storm',
fr: 'Tempête de Poussière',
type: MeteoalarmEventType.Dust,
},
{
en: 'Extreme Cold',
fr: 'Froid Extrême',
type: MeteoalarmEventType.LowTemperature,
},
{
en: 'Flash Freeze',
fr: 'Refroidissement Soudain',
type: MeteoalarmEventType.SnowIce,
},
{
en: 'Fog',
fr: 'Brouillard',
type: MeteoalarmEventType.Fog,
},
{
en: 'Freezing Drizzle',
fr: 'Bruine Verglaçante',
type: MeteoalarmEventType.SnowIce,
},
{
en: 'Freezing Rain',
fr: 'Pluie Verglaçante',
type: MeteoalarmEventType.SnowIce,
},
{
en: 'Frost',
fr: 'Gel',
type: MeteoalarmEventType.SnowIce,
},
{
en: 'Heat',
fr: 'Chaleur',
type: MeteoalarmEventType.HighTemperature,
},
{
en: 'Hurricane',
fr: 'Ouragan',
type: MeteoalarmEventType.Hurricane,
},
{
en: 'Rainfall',
fr: 'Pluie',
type: MeteoalarmEventType.Rain,
},
{
en: 'Severe Thunderstorm',
fr: 'Orages Violents',
type: MeteoalarmEventType.Thunderstorms,
},
{
en: 'Smog',
fr: 'Smog',
type: MeteoalarmEventType.AirQuality,
},
{
en: 'Snowfall',
fr: 'Neige',
type: MeteoalarmEventType.SnowIce,
},
{
en: 'Snow Squall',
fr: 'Bourrasques de Neige',
type: MeteoalarmEventType.SnowIce,
},
{
en: 'Storm Surge',
fr: 'Onde de Tempête',
type: MeteoalarmEventType.Thunderstorms,
},
{
en: 'Tornado',
fr: 'Tornade',
type: MeteoalarmEventType.Tornado,
},
{
en: 'Tropical Storm',
fr: 'Tempête Tropicale',
type: MeteoalarmEventType.Hurricane,
},
{
en: 'Tsunami',
fr: 'Tsunami',
type: MeteoalarmEventType.Tsunami,
},
{
en: 'Weather',
fr: 'Météorologique',
type: MeteoalarmEventType.Unknown,
},
{
en: 'Wind',
fr: 'Vents',
type: MeteoalarmEventType.Wind,
},
{
en: 'Winter Storm',
fr: 'Tempête Hivernale',
type: MeteoalarmEventType.SnowIce,
},
{
en: 'Special Weather',
fr: 'Météorologique Spécial',
type: MeteoalarmEventType.Unknown,
},
{
en: 'Special Air Quality',
fr: "Spécial Sur La Qualité De L'Air",
type: MeteoalarmEventType.AirQuality,
},
];
}
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', 'Avertissement D'],
},
{
type: EnvCanadaEntityType.Watch,
en: ['Watch'],
fr: ['Veille De', "Veille D'"],
},
{
type: EnvCanadaEntityType.Statement,
en: ['Statement'],
fr: ['Bulletin'],
},
{
type: EnvCanadaEntityType.Advisory,
en: ['Advisory'],
fr: ['Avis De', "Avis D'", 'Avis'],
},
];
}
/**
* Alert name in the integration are combined event type
* and sensor from which it was sent like:
* Wind + Warning = Wind Warning
* 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 prefixes = isFrench ? prefixTranslation.fr : prefixTranslation.en;
const prefix = prefixes.find((p) => alertName.includes(p));
if (prefix == undefined) {
throw new Error(
`Failed to match one of the known prefixes to alert name! ` +
`Was looking for [ "${prefixes.join('", "')}" ] and failed ` +
`(isFrench=${isFrench})`,
);
}
alertName = alertName.replace(prefix, '').trim();
return this.eventTypes.find((e) => {
return (isFrench && e.fr == alertName) || (!isFrench && e.en == alertName);
});
}
public getAlerts(entity: EnvCanadaEntity): MeteoalarmAlert[] {
const warningCount = Number(entity.state);
const result: MeteoalarmAlert[] = [];
const type = this.getEntityType(entity)!;
for (let i = 1; i < warningCount + 1; i++) {
const alertName = entity.attributes[`alert_${i}`] as string;
const isFrench = entity.attributes.attribution == 'Données fournies par Environnement Canada';
const alert = this.praseAlertName(alertName, type, isFrench);
if (alert) {
result.push({
level: this.getLevelFromType(type),
event: alert.type,
});
} else {
throw new Error(
`Unknown Environnement Canada alert encountered! Name: ${alertName} (isFrench=${isFrench})`,
);
}
}
return result;
}
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')) {
return EnvCanadaEntityType.Warning;
} else if (attribute.includes('watches')) {
return EnvCanadaEntityType.Watch;
} else if (attribute.includes('statements')) {
return EnvCanadaEntityType.Statement;
} else if (attribute.includes('advisory')) {
return EnvCanadaEntityType.Advisory;
}
}
return undefined;
}
private getLevelFromType(type: EnvCanadaEntityType): MeteoalarmLevelType {
if (type == EnvCanadaEntityType.Warning) {
return MeteoalarmLevelType.Red;
} else if (type == EnvCanadaEntityType.Watch) {
return MeteoalarmLevelType.Orange;
} else if (type == EnvCanadaEntityType.Statement || type == EnvCanadaEntityType.Advisory) {
return MeteoalarmLevelType.Yellow;
} else {
// This should never happen but is required in order for TS to stop complaining
return MeteoalarmLevelType.Red;
}
}
}