From 370820bbfd204ccaaab91a06f151a10fb20525b6 Mon Sep 17 00:00:00 2001 From: MichelFR Date: Thu, 9 Mar 2023 18:29:08 +0100 Subject: [PATCH] Subscribe to Config Entities to delete invalid one #8 --- package-lock.json | 4 ++-- package.json | 2 +- src/index.ts | 31 ++++++++++++++++++++++--------- src/services/DockerService.ts | 33 ++++++++++++++++++++++----------- 4 files changed, 47 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index dd60fd7..6750560 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mqdockerup", - "version": "0.13.4", + "version": "0.14.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mqdockerup", - "version": "0.13.4", + "version": "0.14.0", "license": "MIT", "dependencies": { "axios": "^1.3.4", diff --git a/package.json b/package.json index 00b77a9..020d158 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mqdockerup", - "version": "0.13.4", + "version": "0.14.0", "description": "A Node.js application that pushes information about running Docker containers to an MQTT server.", "main": "index.js", "scripts": { diff --git a/src/index.ts b/src/index.ts index 0ec756a..8c309a3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,6 +15,7 @@ const client = mqtt.connect(config.mqtt.connectionUri, { }); client.subscribe(`${config.mqtt.topic}/update`); +client.subscribe('homeassistant/+/+/+/config'); const checkAndPublishUpdates = async (): Promise => { logger.info("Checking for image updates..."); @@ -44,26 +45,38 @@ client.on("connect", async () => { client.on("message", async (topic: string, message: any) => { const data = JSON.parse(message); - const containerId = data?.containerId; - const image = data?.image; - if ((topic = "mqdockerup/update" && containerId)) { + // Update-Handler for the /update message from MQTT + // This is triggered by the Home Assistant button in the UI to update a container + if ((topic = "mqdockerup/update" && data?.containerId)) { + const image = data?.image; logger.info(`Got update message for ${image}`); - await DockerService.updateContainer(containerId, client); - + await DockerService.updateContainer(data?.containerId, client); logger.info("Updated container "); await checkAndPublishUpdates(); } + + // Missing Docker Container-Handler, removes the /config message from MQTT when the container is missing + // This removes the entity from Home Assistant if the container is not existing anymore + if (data?.device?.manufacturer === "MqDockerUp") { + const image = data?.device?.model; + const containerExists = await DockerService.checkIfContainerExists(image); + + if (!containerExists) { + client.publish(topic, ""); + logger.info(`Removed missing container ${image} from Home Assistant`); + } + } }); -const exitHandler = async (exitCode: number, error?: any) => { +const exitHandler = (exitCode: number, error?: any) => { HomeassistantService.publishAvailability(client, false); const now = new Date().toLocaleString(); let message = exitCode === 0 ? `MqDockerUp gracefully stopped` : `MqDockerUp stopped due to an error`; - - + + if (error) { logger.error(message); logger.error(typeof error); @@ -71,7 +84,7 @@ const exitHandler = async (exitCode: number, error?: any) => { } else { logger.info(message); } - + process.exit(exitCode); }; diff --git a/src/services/DockerService.ts b/src/services/DockerService.ts index e0f9ea0..42a5a9e 100644 --- a/src/services/DockerService.ts +++ b/src/services/DockerService.ts @@ -47,7 +47,7 @@ export default class DockerService { if (response.status === 200) { return { registry: "Docker Hub", response }; } - } catch (error) {} + } catch (error) { } const registryList = [ { @@ -179,7 +179,7 @@ export default class DockerService { logger.error("Stream Error: " + err); return; } - + logger.info("Image pulled successfully"); const containerConfig: any = { ...info, @@ -189,21 +189,21 @@ export default class DockerService { name: info.Name, Image: image, }; - + const mounts = info.Mounts; const binds = mounts.map( (mount) => `${mount.Source}:${mount.Destination}` ); containerConfig.HostConfig.Binds = binds; - + await container.stop(); await container.remove(); - + const newContainer = await DockerService.docker.createContainer( containerConfig ); await newContainer.start(); - + return newContainer; }, function (event) { @@ -213,20 +213,20 @@ export default class DockerService { // get current, total and start values const current = event.progressDetail.current || 0; const total = event.progressDetail.total || 0; - + // calculate percentage based on the difference between the current and last progress events const percentage = current > lastProgressEvent.progressDetail.current ? Math.round(((totalProgress + current - lastProgressEvent.progressDetail.current) / totalSize) * 100) : Math.round((totalProgress / totalSize) * 100); - + // update total progress and size totalProgress += current - lastProgressEvent.progressDetail.current; totalSize += total - lastProgressEvent.progressDetail.total; - + // keep track of the last progress event lastProgressEvent = event; - + // print percentage logger.info(`Total progress: ${totalProgress}/${totalSize} (${percentage}%)`); @@ -308,8 +308,19 @@ export default class DockerService { * @throws An error if the container could not be created. */ public static async createContainer(containerConfig: any): Promise { - const container = await DockerService.docker.createContainer({...containerConfig}); + const container = await DockerService.docker.createContainer({ ...containerConfig }); return container; } + + /** + * Checks if a container exists. + * @param containerImage - The name of the Docker image to check. + * @returns A promise that resolves to true if the container exists. + */ + public static async checkIfContainerExists(containerImage: string): Promise { + const containers = await DockerService.docker.listContainers({ all: true }); + const container = containers.find((container) => container.Image === containerImage); + return container !== undefined; + } }