mirror of https://github.com/Hypfer/Valetudo.git
226 lines
6.5 KiB
JavaScript
226 lines
6.5 KiB
JavaScript
const Logger = require("./Logger");
|
|
const Tools = require("./utils/Tools");
|
|
|
|
const Bonjour = require("bonjour-service");
|
|
const SSDPServer = require("./utils/SSDPServer");
|
|
|
|
const NETWORK_STATE_CHECK_INTERVAL = 30 * 1000;
|
|
|
|
class NetworkAdvertisementManager {
|
|
/**
|
|
* This class handles advertisement via both SSDP (UPnP) and zeroconf/mdns/bonjour
|
|
*
|
|
* @param {object} options
|
|
* @param {import("./Configuration")} options.config
|
|
* @param {import("./core/ValetudoRobot")} options.robot
|
|
* @param {import("./utils/ValetudoHelper")} options.valetudoHelper
|
|
*/
|
|
constructor(options) {
|
|
this.config = options.config;
|
|
this.robot = options.robot;
|
|
this.valetudoHelper = options.valetudoHelper;
|
|
|
|
this.webserverPort = this.config.get("webserver")?.port ?? 80;
|
|
|
|
this.networkStateCheckTimeout = undefined;
|
|
this.ipAddresses = "";
|
|
|
|
this.config.onUpdate((key) => {
|
|
if (key === "networkAdvertisement") {
|
|
this.restart().catch((err) => {
|
|
Logger.warn("Error while restarting NetworkAdvertisementManager due to config change", err);
|
|
});
|
|
}
|
|
});
|
|
|
|
this.valetudoHelper.onFriendlyNameChanged(() => {
|
|
this.restart().catch((err) => {
|
|
Logger.warn("Error while restarting NetworkAdvertisementManager due to friendly name change", err);
|
|
});
|
|
});
|
|
|
|
this.setUp();
|
|
}
|
|
|
|
/**
|
|
* @public
|
|
* @return {{port: number, zeroconfHostname: string}}
|
|
*/
|
|
getProperties() {
|
|
return {
|
|
port: this.webserverPort,
|
|
zeroconfHostname: Tools.GET_ZEROCONF_HOSTNAME()
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
setUp() {
|
|
const networkAdvertisementConfig = this.config.get("networkAdvertisement");
|
|
|
|
if (this.config.get("embedded") === true) {
|
|
if (networkAdvertisementConfig.enabled === true) {
|
|
this.setUpSSDP();
|
|
this.setUpBonjour();
|
|
|
|
this.ipAddresses = Tools.GET_CURRENT_HOST_IP_ADDRESSES().sort().join();
|
|
|
|
clearTimeout(this.networkStateCheckTimeout);
|
|
this.networkStateCheckTimeout = setTimeout(() => {
|
|
this.checkNetworkStateAndReschedule();
|
|
}, NETWORK_STATE_CHECK_INTERVAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
setUpSSDP() {
|
|
this.ssdpServer = new SSDPServer({
|
|
port: this.webserverPort
|
|
});
|
|
|
|
try {
|
|
this.ssdpServer.start();
|
|
} catch (e) {
|
|
Logger.warn("Error while starting SSDP/UPnP advertisement", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
setUpBonjour() {
|
|
this.bonjourServer = new Bonjour.Bonjour(undefined, (err) => {
|
|
Logger.warn("Error while responding to mDNS query:", err);
|
|
|
|
this.restart().catch(err => {
|
|
this.shutdown().catch(err => {
|
|
throw err;
|
|
});
|
|
});
|
|
});
|
|
|
|
Logger.info("Valetudo can be reached via: " + Tools.GET_ZEROCONF_HOSTNAME());
|
|
|
|
this.publishBonjourService(`Valetudo ${Tools.GET_HUMAN_READABLE_SYSTEM_ID()} Web`, "http");
|
|
this.publishBonjourService(`Valetudo ${Tools.GET_HUMAN_READABLE_SYSTEM_ID()}`, "valetudo");
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* @param {string} name
|
|
* @param {string} type
|
|
*/
|
|
publishBonjourService(name, type) {
|
|
const txtRecords = {
|
|
id: Tools.GET_HUMAN_READABLE_SYSTEM_ID(),
|
|
model: this.robot.getModelName(),
|
|
manufacturer: this.robot.getManufacturer(),
|
|
version: Tools.GET_VALETUDO_VERSION(),
|
|
};
|
|
|
|
if (this.valetudoHelper.hasFriendlyName()) {
|
|
txtRecords["name"] = this.valetudoHelper.getFriendlyName();
|
|
}
|
|
|
|
const service = this.bonjourServer.publish({
|
|
name: name,
|
|
type: type,
|
|
host: Tools.GET_ZEROCONF_HOSTNAME(),
|
|
port: this.webserverPort,
|
|
probe: false,
|
|
txt: txtRecords
|
|
});
|
|
|
|
|
|
service.on("up", () => {
|
|
Logger.info("Bonjour service \"" + name + "\" with type \"" + type + "\" started");
|
|
});
|
|
|
|
service.on("error", (err) => {
|
|
Logger.warn("Error while starting Bonjour service \"" + name + "\" with type \"" + type + "\"", err);
|
|
});
|
|
|
|
service.start();
|
|
}
|
|
|
|
/**
|
|
* Shutdown NetworkAdvertisementManager
|
|
*
|
|
* @public
|
|
* @returns {Promise<void>}
|
|
*/
|
|
shutdown() {
|
|
return new Promise((resolve, reject) => {
|
|
Logger.debug("NetworkAdvertisementManager shutdown in progress...");
|
|
clearTimeout(this.networkStateCheckTimeout);
|
|
|
|
if (this.ssdpServer) {
|
|
try {
|
|
this.ssdpServer.stop();
|
|
} catch (err) {
|
|
Logger.warn("Error while stopping SSDP Server", err);
|
|
}
|
|
}
|
|
|
|
if (this.bonjourServer) {
|
|
this.bonjourServer.unpublishAll(() => {
|
|
this.bonjourServer.destroy();
|
|
|
|
Logger.debug("NetworkAdvertisementManager shutdown done");
|
|
resolve();
|
|
});
|
|
} else {
|
|
Logger.debug("NetworkAdvertisementManager shutdown done");
|
|
|
|
resolve();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Restart NetworkAdvertisementManager
|
|
*
|
|
* @private
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async restart() {
|
|
Logger.info("Restarting NetworkAdvertisementManager");
|
|
|
|
try {
|
|
await this.shutdown();
|
|
this.setUp();
|
|
} catch (err) {
|
|
Logger.error("Error while restarting NetworkAdvertisementManager", err);
|
|
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
checkNetworkStateAndReschedule() {
|
|
const ipAddresses = Tools.GET_CURRENT_HOST_IP_ADDRESSES().sort().join();
|
|
|
|
if (this.ipAddresses !== ipAddresses) {
|
|
Logger.info("Network state changed");
|
|
|
|
this.restart().catch((err) => {
|
|
Logger.warn("Error while restarting NetworkAdvertisementManager due to network state change", err);
|
|
});
|
|
} else {
|
|
clearTimeout(this.networkStateCheckTimeout);
|
|
|
|
this.networkStateCheckTimeout = setTimeout(() => {
|
|
this.checkNetworkStateAndReschedule();
|
|
}, NETWORK_STATE_CHECK_INTERVAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = NetworkAdvertisementManager;
|