initial commit
This commit is contained in:
8
src/clients/mqtt/mqtt-client.schema.ts
Normal file
8
src/clients/mqtt/mqtt-client.schema.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import z from "zod";
|
||||
|
||||
export const MQTTClientOptions = z.object({
|
||||
password: z.string().optional(),
|
||||
user: z.string().optional(),
|
||||
host: z.string(),
|
||||
port: z.number(),
|
||||
});
|
||||
12
src/clients/mqtt/mqtt-client.ts
Normal file
12
src/clients/mqtt/mqtt-client.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import mqtt from "mqtt";
|
||||
import type z from "zod";
|
||||
import type { MQTTClientOptions } from "./mqtt-client.schema";
|
||||
|
||||
export async function getMqttClient(
|
||||
options: z.output<typeof MQTTClientOptions>,
|
||||
) {
|
||||
return await mqtt.connectAsync(`mqtt://${options.host}:${options.port}`, {
|
||||
password: options.password,
|
||||
username: options.user,
|
||||
});
|
||||
}
|
||||
11
src/config.schema.ts
Normal file
11
src/config.schema.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import z from "zod";
|
||||
import { MQTTClientOptions } from "./clients/mqtt/mqtt-client.schema";
|
||||
|
||||
export const ConfigSchema = z.object({
|
||||
mqtt: MQTTClientOptions,
|
||||
publishInterval: z.number().default(2000),
|
||||
device: z.object({
|
||||
key: z.string(),
|
||||
name: z.string(),
|
||||
}),
|
||||
});
|
||||
1
src/constants.ts
Normal file
1
src/constants.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const APPLICATION_NAME = "efibootmgr-mqtt";
|
||||
8
src/efibootmgr.schema.ts
Normal file
8
src/efibootmgr.schema.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import z from "zod";
|
||||
|
||||
export const BootEntry = z.object({
|
||||
label: z.string(),
|
||||
number: z.number(),
|
||||
});
|
||||
|
||||
export const BootEntries = z.array(BootEntry);
|
||||
21
src/efibootmgr.service.ts
Normal file
21
src/efibootmgr.service.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { $ } from "bun";
|
||||
import type z from "zod";
|
||||
import type { BootEntries } from "./efibootmgr.schema";
|
||||
|
||||
export class EfiBootMgrService {
|
||||
async reboot() {
|
||||
await $`reboot`;
|
||||
}
|
||||
|
||||
async setNextBoot(bootNum: string) {
|
||||
await $`efibootmgr -n ${bootNum}`;
|
||||
}
|
||||
|
||||
async listBootEntries(): Promise<z.output<typeof BootEntries>> {
|
||||
const output = await $`efibootmgr`.text();
|
||||
|
||||
console.log(output);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
34
src/index.ts
Normal file
34
src/index.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { getMqttClient } from "./clients/mqtt/mqtt-client";
|
||||
import { ConfigSchema } from "./config.schema";
|
||||
import { EfiBootMgrService } from "./efibootmgr.service";
|
||||
import { startListeners } from "./listener";
|
||||
import { logger } from "./logger";
|
||||
import { runPublishLoop } from "./publisher";
|
||||
|
||||
logger.info("Starting intialization...");
|
||||
|
||||
const configsFolder = process.env.CONFIGS_PATH || "./";
|
||||
|
||||
const fileContent = await readFile(
|
||||
join(configsFolder, "efibootmgr.config.json"),
|
||||
{
|
||||
encoding: "utf8",
|
||||
},
|
||||
).catch((e) => {
|
||||
throw new Error("Error while reading config!", { cause: e });
|
||||
});
|
||||
|
||||
const json = JSON.parse(fileContent);
|
||||
|
||||
const config = ConfigSchema.parse(json);
|
||||
|
||||
const efiBootMgrService = new EfiBootMgrService();
|
||||
|
||||
const bootEntries = await efiBootMgrService.listBootEntries();
|
||||
|
||||
const mqttClient = await getMqttClient(config.mqtt);
|
||||
|
||||
runPublishLoop(config, mqttClient, bootEntries);
|
||||
startListeners(config, mqttClient);
|
||||
36
src/listener.ts
Normal file
36
src/listener.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { MqttClient } from "mqtt";
|
||||
import type z from "zod";
|
||||
import type { ConfigSchema } from "./config.schema";
|
||||
import { logger } from "./logger";
|
||||
|
||||
export async function startListeners(
|
||||
config: z.output<typeof ConfigSchema>,
|
||||
mqttClient: MqttClient,
|
||||
) {
|
||||
await mqttClient.subscribeAsync(`efibootmgr-mqtt/${config.device.key}/+`);
|
||||
|
||||
mqttClient.on("message", async (topic, payload) => {
|
||||
logger.info(topic, payload);
|
||||
|
||||
const pathParts = topic.split("/");
|
||||
|
||||
const action = pathParts[pathParts.length - 1];
|
||||
|
||||
switch (action) {
|
||||
/* case "trigger-power": {
|
||||
await nanoKvmService.triggerPower({});
|
||||
|
||||
return;
|
||||
}
|
||||
case "trigger-reset": {
|
||||
await nanoKvmService.triggerReset();
|
||||
|
||||
return;
|
||||
}
|
||||
*/
|
||||
default: {
|
||||
throw new Error(`Unhandled action: ${action}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
6
src/logger.ts
Normal file
6
src/logger.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Logger } from "tslog";
|
||||
import { APPLICATION_NAME } from "./constants";
|
||||
|
||||
export const logger = new Logger({
|
||||
name: APPLICATION_NAME,
|
||||
});
|
||||
65
src/publisher.ts
Normal file
65
src/publisher.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { sleep } from "bun";
|
||||
import type { MqttClient } from "mqtt";
|
||||
import type z from "zod";
|
||||
import type { ConfigSchema } from "./config.schema";
|
||||
import { APPLICATION_NAME } from "./constants";
|
||||
import type { BootEntries } from "./efibootmgr.schema";
|
||||
import { logger } from "./logger";
|
||||
|
||||
export async function runPublishLoop(
|
||||
config: z.output<typeof ConfigSchema>,
|
||||
mqttClient: MqttClient,
|
||||
bootEntries: z.output<typeof BootEntries>,
|
||||
) {
|
||||
while (true) {
|
||||
/* const origin = {
|
||||
name: APPLICATION_NAME,
|
||||
sw: "0.0.1",
|
||||
url: "https://git.unom.io/enricobuehler/efibootmgr-mqtt",
|
||||
}; */
|
||||
|
||||
const state_topic = `${APPLICATION_NAME}/${config.device.key}`;
|
||||
|
||||
const entityIds = {
|
||||
actions: {
|
||||
triggerReboot: "action_trigger_reboot",
|
||||
},
|
||||
};
|
||||
|
||||
const device = {
|
||||
hw_version: 0,
|
||||
identifiers: [config.device.key],
|
||||
manufacturer: "unknown",
|
||||
model: "unknown",
|
||||
model_id: "unknown",
|
||||
name: config.device.name,
|
||||
sw_version: "1.0.0",
|
||||
};
|
||||
|
||||
// Actions
|
||||
|
||||
for (const bootEntry of bootEntries) {
|
||||
await mqttClient.publishAsync(
|
||||
`homeassistant/button/${config.device.key}/button-trigger-reboot/config`,
|
||||
JSON.stringify({
|
||||
device,
|
||||
command_topic: `${state_topic}/trigger-reboot`,
|
||||
default_entity_id: `button.${config.device.key}_${entityIds.actions.triggerReboot}`,
|
||||
name: `Reboot into ${bootEntry.label}`,
|
||||
object_id: `efibootmgr_${config.device.key}_${entityIds.actions.triggerReboot}`,
|
||||
payload_press: bootEntry.number,
|
||||
unique_id: `${config.device.key}_efibootmgr_${entityIds.actions.triggerReboot}`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
logger.debug("AutoDiscovery - Published actions");
|
||||
|
||||
//console.log(info.ips);
|
||||
//console.log(gpio);
|
||||
|
||||
await sleep(config.publishInterval);
|
||||
}
|
||||
}
|
||||
7
src/scripts/generate-config-json-schema.ts
Normal file
7
src/scripts/generate-config-json-schema.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import z from "zod";
|
||||
import { ConfigSchema } from "@/config.schema";
|
||||
|
||||
const jsonSchema = z.toJSONSchema(ConfigSchema);
|
||||
|
||||
await writeFile("./config-schema.json", JSON.stringify(jsonSchema));
|
||||
Reference in New Issue
Block a user