Add dashboard
parent
ae458c8046
commit
4613025579
@ -0,0 +1,14 @@
|
||||
import { HA_TOKEN, HA_URL } from "./config.js";
|
||||
|
||||
export type HA_RESPONSE = { state: string; attributes: Record<string, unknown> };
|
||||
|
||||
export async function get_state(entity: string): Promise<HA_RESPONSE> {
|
||||
const response = (await (
|
||||
await fetch(`${HA_URL}/api/states/${entity}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${HA_TOKEN}`,
|
||||
},
|
||||
})
|
||||
).json()) as HA_RESPONSE;
|
||||
return response;
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
import { TempstreamJSX, Templatable } from "tempstream";
|
||||
import { BaseContext } from "koa";
|
||||
import { StatefulPage } from "@sealcode/sealgen";
|
||||
import html from "../html.js";
|
||||
import { get_state, HA_RESPONSE } from "../ha_api.js";
|
||||
import { sleep } from "../util.js";
|
||||
|
||||
export const actionName = "Dashboard";
|
||||
|
||||
const actions = {
|
||||
add: (state: State, inputs: Record<string, string>) => {
|
||||
console.log({ inputs });
|
||||
return {
|
||||
...state,
|
||||
elements: [...state.elements, inputs.element_to_add || "new element"],
|
||||
};
|
||||
},
|
||||
remove: (state: State, _: unknown, index_to_remove: number) => {
|
||||
return {
|
||||
...state,
|
||||
elements: state.elements.filter((_, index) => index != index_to_remove),
|
||||
};
|
||||
},
|
||||
} as const;
|
||||
|
||||
type State = {
|
||||
elements: string[];
|
||||
};
|
||||
|
||||
async function with_unit(s: HA_RESPONSE | Promise<HA_RESPONSE>) {
|
||||
s = await s;
|
||||
return `${s.state}${s.attributes.unit_of_measurement as string}`;
|
||||
}
|
||||
|
||||
export default new (class DashboardPage extends StatefulPage<State, typeof actions> {
|
||||
actions = actions;
|
||||
|
||||
getInitialState() {
|
||||
return { elements: ["one", "two", "three"] };
|
||||
}
|
||||
|
||||
wrapInLayout(ctx: BaseContext, content: Templatable): Templatable {
|
||||
return html(ctx, "Dashboard", content, { navbar: () => "" });
|
||||
}
|
||||
|
||||
render(ctx: BaseContext, state: State, inputs: Record<string, string>) {
|
||||
const date = new Date();
|
||||
const stuff = [
|
||||
{ label: "Temperatura na balkonie", entity: "input_number.balkon_average" },
|
||||
{ label: "Temperatura w salonie", entity: "sensor.ewelink_th01_temperature" },
|
||||
{ label: "Wilgotność w salonie", entity: "sensor.ewelink_th01_humidity" },
|
||||
{ label: "Temperatura na strychu", entity: "sensor.strych_temperature_3" },
|
||||
{
|
||||
label: "Docelowa temp. na strychu",
|
||||
entity: "input_number.strych_target_temperature",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div style="font-size: 2rem">
|
||||
<div style="font-size: 10rem; text-align: center;">
|
||||
{date.getHours()}:{date.getMinutes().toString().padStart(2, "0")}
|
||||
</div>
|
||||
<table style="margin: 0 auto">
|
||||
<tbody>
|
||||
{stuff.map(({ label, entity }) => (
|
||||
<tr>
|
||||
<td style="text-align: right">{label}:</td>
|
||||
<td>
|
||||
<strong>{with_unit(get_state(entity))}</strong>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{
|
||||
/* HTML */ `<script>
|
||||
setInterval(function () {
|
||||
document.location = document.location;
|
||||
}, 15 * 1000);
|
||||
</script>`
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})();
|
@ -0,0 +1,48 @@
|
||||
import { withProdApp } from "../test_utils/with-prod-app.js";
|
||||
import { VERY_LONG_TEST_TIMEOUT, webhintURL } from "../test_utils/webhint.js";
|
||||
import { DashboardURL } from "./urls.js";
|
||||
import { getBrowser } from "../test_utils/browser-creator.js";
|
||||
import { Browser, BrowserContext, Page } from "@playwright/test";
|
||||
|
||||
describe("Dashboard webhint", () => {
|
||||
it(
|
||||
"doesn't crash",
|
||||
async function () {
|
||||
return withProdApp(async ({ base_url, rest_api }) => {
|
||||
await rest_api.get(DashboardURL);
|
||||
await webhintURL(base_url + DashboardURL);
|
||||
// alternatively you can use webhintHTML for faster but less precise scans
|
||||
// or for scanning responses of requests that use some form of authorization:
|
||||
// const response = await rest_api.get(DashboardURL);
|
||||
// await webhintHTML(response);
|
||||
});
|
||||
},
|
||||
VERY_LONG_TEST_TIMEOUT
|
||||
);
|
||||
});
|
||||
|
||||
describe("Dashboard", () => {
|
||||
let page: Page;
|
||||
let browser: Browser;
|
||||
let context: BrowserContext;
|
||||
|
||||
beforeEach(async () => {
|
||||
browser = await getBrowser();
|
||||
context = await browser.newContext();
|
||||
page = await context.newPage();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it(
|
||||
"works as expected",
|
||||
async function () {
|
||||
return withProdApp(async ({ base_url }) => {
|
||||
await page.goto(base_url + DashboardURL);
|
||||
});
|
||||
},
|
||||
VERY_LONG_TEST_TIMEOUT
|
||||
);
|
||||
});
|
Loading…
Reference in New Issue