221 lines
5.9 KiB
TypeScript
221 lines
5.9 KiB
TypeScript
import {
|
|
LovelaceConfig,
|
|
LovelaceRawConfig,
|
|
isStrategyDashboard,
|
|
} from "../../../data/lovelace/config/types";
|
|
import { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
|
import {
|
|
LovelaceViewConfig,
|
|
isStrategyView,
|
|
} from "../../../data/lovelace/config/view";
|
|
import { AsyncReturnType, HomeAssistant } from "../../../types";
|
|
import { cleanLegacyStrategyConfig, isLegacyStrategy } from "./legacy-strategy";
|
|
import {
|
|
LovelaceDashboardStrategy,
|
|
LovelaceSectionStrategy,
|
|
LovelaceStrategy,
|
|
LovelaceViewStrategy,
|
|
} from "./types";
|
|
|
|
const MAX_WAIT_STRATEGY_LOAD = 5000;
|
|
const CUSTOM_PREFIX = "custom:";
|
|
|
|
const STRATEGIES: Record<LovelaceStrategyConfigType, Record<string, any>> = {
|
|
dashboard: {
|
|
"original-states": () => import("./original-states-dashboard-strategy"),
|
|
},
|
|
view: {
|
|
"original-states": () => import("./original-states-view-strategy"),
|
|
energy: () => import("../../energy/strategies/energy-view-strategy"),
|
|
},
|
|
section: {},
|
|
};
|
|
|
|
export type LovelaceStrategyConfigType = "dashboard" | "view" | "section";
|
|
|
|
type Strategies = {
|
|
dashboard: LovelaceDashboardStrategy;
|
|
view: LovelaceViewStrategy;
|
|
section: LovelaceSectionStrategy;
|
|
};
|
|
|
|
type StrategyConfig<T extends LovelaceStrategyConfigType> = AsyncReturnType<
|
|
Strategies[T]["generate"]
|
|
>;
|
|
|
|
export const getLovelaceStrategy = async <T extends LovelaceStrategyConfigType>(
|
|
configType: T,
|
|
strategyType: string
|
|
): Promise<LovelaceStrategy> => {
|
|
if (strategyType in STRATEGIES[configType]) {
|
|
await STRATEGIES[configType][strategyType]();
|
|
const tag = `${strategyType}-${configType}-strategy`;
|
|
return customElements.get(tag) as unknown as Strategies[T];
|
|
}
|
|
|
|
if (!strategyType.startsWith(CUSTOM_PREFIX)) {
|
|
throw new Error("Unknown strategy");
|
|
}
|
|
|
|
const legacyTag = `ll-strategy-${strategyType.slice(CUSTOM_PREFIX.length)}`;
|
|
const tag = `ll-strategy-${configType}-${strategyType.slice(
|
|
CUSTOM_PREFIX.length
|
|
)}`;
|
|
|
|
if (
|
|
(await Promise.race([
|
|
customElements.whenDefined(legacyTag),
|
|
customElements.whenDefined(tag),
|
|
new Promise((resolve) => {
|
|
setTimeout(() => resolve(true), MAX_WAIT_STRATEGY_LOAD);
|
|
}),
|
|
])) === true
|
|
) {
|
|
throw new Error(
|
|
`Timeout waiting for strategy element ${tag} to be registered`
|
|
);
|
|
}
|
|
|
|
return (customElements.get(tag) ??
|
|
customElements.get(legacyTag)) as unknown as Strategies[T];
|
|
};
|
|
|
|
const generateStrategy = async <T extends LovelaceStrategyConfigType>(
|
|
configType: T,
|
|
renderError: (err: string | Error) => StrategyConfig<T>,
|
|
strategyConfig: LovelaceStrategyConfig,
|
|
hass: HomeAssistant
|
|
): Promise<StrategyConfig<T>> => {
|
|
const strategyType = strategyConfig.type;
|
|
if (!strategyType) {
|
|
// @ts-ignore
|
|
return renderError("No strategy type found");
|
|
}
|
|
|
|
try {
|
|
const strategy = await getLovelaceStrategy<T>(configType, strategyType);
|
|
|
|
// Backward compatibility for custom strategies for loading old strategies format
|
|
if (isLegacyStrategy(strategy)) {
|
|
if (configType === "dashboard" && "generateDashboard" in strategy) {
|
|
return (await strategy.generateDashboard({
|
|
config: { strategy: strategyConfig, views: [] },
|
|
hass,
|
|
})) as StrategyConfig<T>;
|
|
}
|
|
if (configType === "view" && "generateView" in strategy) {
|
|
return (await strategy.generateView({
|
|
config: { views: [] },
|
|
view: { strategy: strategyConfig },
|
|
hass,
|
|
})) as StrategyConfig<T>;
|
|
}
|
|
}
|
|
|
|
const config = cleanLegacyStrategyConfig(strategyConfig);
|
|
|
|
return await strategy.generate(config, hass);
|
|
} catch (err: any) {
|
|
if (err.message !== "timeout") {
|
|
// eslint-disable-next-line
|
|
console.error(err);
|
|
}
|
|
// @ts-ignore
|
|
return renderError(err);
|
|
}
|
|
};
|
|
|
|
export const generateLovelaceDashboardStrategy = async (
|
|
strategyConfig: LovelaceStrategyConfig,
|
|
hass: HomeAssistant
|
|
): Promise<LovelaceConfig> =>
|
|
generateStrategy(
|
|
"dashboard",
|
|
(err) => ({
|
|
views: [
|
|
{
|
|
title: "Error",
|
|
cards: [
|
|
{
|
|
type: "markdown",
|
|
content: `Error loading the dashboard strategy:\n> ${err}`,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}),
|
|
strategyConfig,
|
|
hass
|
|
);
|
|
|
|
export const generateLovelaceViewStrategy = async (
|
|
strategyConfig: LovelaceStrategyConfig,
|
|
hass: HomeAssistant
|
|
): Promise<LovelaceViewConfig> =>
|
|
generateStrategy(
|
|
"view",
|
|
(err) => ({
|
|
cards: [
|
|
{
|
|
type: "markdown",
|
|
content: `Error loading the view strategy:\n> ${err}`,
|
|
},
|
|
],
|
|
}),
|
|
strategyConfig,
|
|
hass
|
|
);
|
|
|
|
export const generateLovelaceSectionStrategy = async (
|
|
strategyConfig: LovelaceStrategyConfig,
|
|
hass: HomeAssistant
|
|
): Promise<LovelaceViewConfig> =>
|
|
generateStrategy(
|
|
"section",
|
|
(err) => ({
|
|
cards: [
|
|
{
|
|
type: "markdown",
|
|
content: `Error loading the section strategy:\n> ${err}`,
|
|
},
|
|
],
|
|
}),
|
|
strategyConfig,
|
|
hass
|
|
);
|
|
|
|
/**
|
|
* Find all references to strategies and replaces them with the generated output
|
|
*/
|
|
export const expandLovelaceConfigStrategies = async (
|
|
config: LovelaceRawConfig,
|
|
hass: HomeAssistant
|
|
): Promise<LovelaceConfig> => {
|
|
const newConfig = isStrategyDashboard(config)
|
|
? await generateLovelaceDashboardStrategy(config.strategy, hass)
|
|
: { ...config };
|
|
|
|
newConfig.views = await Promise.all(
|
|
newConfig.views.map(async (view) => {
|
|
const newView = isStrategyView(view)
|
|
? await generateLovelaceViewStrategy(view.strategy, hass)
|
|
: { ...view };
|
|
|
|
if (newView.sections) {
|
|
newView.sections = await Promise.all(
|
|
newView.sections.map(async (section) => {
|
|
const newSection = isStrategyView(section)
|
|
? await generateLovelaceSectionStrategy(section.strategy, hass)
|
|
: { ...section };
|
|
return newSection;
|
|
})
|
|
);
|
|
}
|
|
|
|
return newView;
|
|
})
|
|
);
|
|
|
|
return newConfig;
|
|
};
|