diff --git a/.github/workflows/format-code.yml b/.github/workflows/format-code.yml index f4f3913..eb5a091 100644 --- a/.github/workflows/format-code.yml +++ b/.github/workflows/format-code.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Format Code with Prettier run: npx prettier --write "**/*.js" --tab-width diff --git a/MMM-pihole-stats.js b/MMM-pihole-stats.js index 7a6e402..560836a 100644 --- a/MMM-pihole-stats.js +++ b/MMM-pihole-stats.js @@ -1,4 +1,4 @@ -/* Magic Mirror +/* MagicMirror² * Module: Pi-Hole Stats * * By Sheya Bernstein https://github.com/sheyabernstein/MMM-pihole-stats @@ -23,22 +23,21 @@ Module.register("MMM-pihole-stats", { initialLoadDelay: 0, }, - formatInt: function (n) { + formatInt (n) { return n.toLocaleString(); }, - formatFloat: function (n) { + formatFloat (n) { if (this.config.floatingPoints) { - let x = 10 ** this.config.floatingPoints; + const x = 10 ** this.config.floatingPoints; return Math.round(parseFloat(n) * x) / x; - } else { - return n; } + return n; }, // Define start sequence. - start: function () { - Log.info("Starting module: " + this.name); + start () { + Log.info(`Starting module: ${this.name}`); this.domains_being_blocked = null; this.dns_queries_today = null; @@ -51,8 +50,8 @@ Module.register("MMM-pihole-stats", { }, // Override dom generator. - getDom: function () { - let wrapper = document.createElement("div"); + getDom () { + const wrapper = document.createElement("div"); if (!this.loaded) { wrapper.innerHTML = this.translate("LOADING..."); @@ -60,79 +59,79 @@ Module.register("MMM-pihole-stats", { return wrapper; } - let header = document.createElement("div"); + const header = document.createElement("div"); header.className = "small bright"; - header.innerHTML = - this.formatInt(this.ads_blocked_today) + - " ads blocked today. (" + - this.formatFloat(this.ads_percentage_today) + - "%)"; + header.innerHTML + = `${this.formatInt(this.ads_blocked_today)} + ads blocked today. ( + ${this.formatFloat(this.ads_percentage_today)} + %)`; wrapper.appendChild(header); if (this.top_sources && Object.keys(this.top_sources).length) { - let table = document.createElement("table"); + const table = document.createElement("table"); table.className = "xsmall light"; wrapper.appendChild(table); - let thead = document.createElement("thead"); + const thead = document.createElement("thead"); table.appendChild(thead); - let row = document.createElement("tr"); + const row = document.createElement("tr"); thead.appendChild(row); - let sourceCell = document.createElement("th"); + const sourceCell = document.createElement("th"); sourceCell.innerHTML = "Client"; row.appendChild(sourceCell); - let countCell = document.createElement("th"); + const countCell = document.createElement("th"); countCell.innerHTML = "Requests"; row.appendChild(countCell); - let tbody = document.createElement("tbody"); + const tbody = document.createElement("tbody"); table.appendChild(tbody); for (let source in this.top_sources) { - let adCount = this.top_sources[source]; + const adCount = this.top_sources[source]; if (this.config.showSourceHostnameOnly) { source = source.split("|")[0]; } - let row = document.createElement("tr"); + const row = document.createElement("tr"); tbody.appendChild(row); - let sourceCell = document.createElement("td"); + const sourceCell = document.createElement("td"); sourceCell.innerHTML = source; row.appendChild(sourceCell); - let countCell = document.createElement("td"); + const countCell = document.createElement("td"); countCell.innerHTML = this.formatInt(adCount); row.appendChild(countCell); } } - let footer = document.createElement("div"); + const footer = document.createElement("div"); footer.className = "xsmall"; - footer.innerHTML = - this.formatInt(this.dns_queries_today) + - " DNS queries, " + - this.formatInt(this.domains_being_blocked) + - " domains blacklisted."; + footer.innerHTML + = `${this.formatInt(this.dns_queries_today)} + DNS queries, + ${this.formatInt(this.domains_being_blocked)} + domains blacklisted.`; wrapper.appendChild(footer); return wrapper; }, - updateStats: function () { - Log.info(this.name + ": Getting data"); + updateStats () { + Log.info(`${this.name}: Getting data`); this.sendSocketNotification("GET_PIHOLE", { - config: this.config, + config: this.config }); }, // Handle node helper response - socketNotificationReceived: function (notification, payload) { + socketNotificationReceived (notification, payload) { if (notification === "PIHOLE_DATA") { this.processSummary(payload); this.loaded = true; @@ -143,37 +142,37 @@ Module.register("MMM-pihole-stats", { this.updateDom(this.config.animationSpeed); }, - scheduleUpdate: function (delay) { + scheduleUpdate (delay) { let nextLoad = this.config.updateInterval; if (typeof delay !== "undefined" && delay >= 0) { nextLoad = delay; } - let self = this; - setTimeout(function () { + const self = this; + setTimeout(() => { self.updateStats(); self.scheduleUpdate(self.config.updateInterval); }, nextLoad); }, - processSummary: function (data) { + processSummary (data) { if (!data) { // Did not receive usable new data. return; } - this.domains_being_blocked = data["domains_being_blocked"] || "0"; - this.dns_queries_today = data["dns_queries_today"] || "0"; - this.ads_blocked_today = data["ads_blocked_today"] || "0"; - this.ads_percentage_today = data["ads_percentage_today"] || "0.0"; + this.domains_being_blocked = data.domains_being_blocked || "0"; + this.dns_queries_today = data.dns_queries_today || "0"; + this.ads_blocked_today = data.ads_blocked_today || "0"; + this.ads_percentage_today = data.ads_percentage_today || "0.0"; }, - processSources: function (data) { + processSources (data) { if (!data) { // Did not receive usable new data. return; } - this.top_sources = data["top_sources"] || []; + this.top_sources = data.top_sources || []; }, }); diff --git a/README.md b/README.md index 98bbf91..13a3c4a 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MMM-pihole-stats -Pi-hole stats module for MagicMirror2 +Pi-hole stats module for MagicMirror². ## Screenshots @@ -14,21 +14,19 @@ Without `config.showSources` enabled: ## Dependencies -- [MagicMirror2](https://github.com/MichMich/MagicMirror) +- [MagicMirror²](https://github.com/MagicMirrorOrg/MagicMirror) - [Pi-hole](https://pi-hole.net) ## Installation -1. Clone this repo into `~/MagicMirror/modules` directory.
- `git clone https://github.com/sheyabernstein/MMM-pihole-stats.git` -2. Run `npm install` in the cloned module directory -3. Obtain an API token from your PiHole installation by navigating to [http://pi.hole/admin/settings.php?tab=api](http://pi.hole/admin/settings.php?tab=api) and clicking `Show API token` -4. Configure your `~/MagicMirror/config/config.js` - +1. Clone this repo into `~/MagicMirror/modules` directory. + `git clone https://github.com/sheyabernstein/MMM-pihole-stats` +2. Obtain an API token from your PiHole installation by navigating to [http://pi.hole/admin/settings.php?tab=api](http://pi.hole/admin/settings.php?tab=api) and clicking `Show API token` +3. Configure your `~/MagicMirror/config/config.js` Here is an example entry for `config.js`: -``` +```js { module: "MMM-pihole-stats", position: "top_left", // Or any valid MagicMirror position. @@ -36,13 +34,9 @@ Here is an example entry for `config.js`: apiToken: "0123456789abcdef" // See 'Configuration options' for more information. } -} +}, ``` -> Feb 27, 2024 update: This module now requires `npm install` when installing. - -> Sep 27, 2020 update: Configuring the Pi-hole server to allow CORS is no longer needed. - ## Configuration Options | **Option** | **Default** | **Description** | @@ -55,3 +49,9 @@ Here is an example entry for `config.js`: | `updateInterval` | `600000` | Time in ms to wait until updating | | `retryDelay` | `30000` | Time in ms to wait before retry | | `floatingPoints` | `2` | Format floating point numbers to decimal points, e.g. `2` to format to 5.55. Setting this to `0` will show unlimited decimal points | + +## Notes + +- Feb 28, 2024 update: This module needs no external packages anymore. +- Feb 27, 2024 update: This module now requires `npm install` when installing. +- Sep 27, 2020 update: Configuring the Pi-hole server to allow CORS is no longer needed. diff --git a/node_helper.js b/node_helper.js index 63c09a2..8f49883 100644 --- a/node_helper.js +++ b/node_helper.js @@ -1,42 +1,38 @@ const Log = require("logger"); -const request = require("request"); const NodeHelper = require("node_helper"); module.exports = NodeHelper.create({ - start: function () { - Log.info("Starting node_helper for module [" + this.name + "]"); + start () { + Log.info(`Starting node_helper for module [${this.name}]`); }, - socketNotificationReceived: function (notification, payload) { + socketNotificationReceived (notification, payload) { if (notification === "GET_PIHOLE") { - let config = payload.config; + const config = payload.config; if (!this.isValidURL(config.apiURL)) { - Log.error(this.name + ": The apiURL is not a valid URL"); + Log.error(`${this.name}: The apiURL is not a valid URL`); return; } - Log.info("Notification: " + notification + " Payload: " + payload); + Log.debug(`Notification: ${notification} Payload: ${payload}`); this.getPiholeData(config, { summary: 1 }, "PIHOLE_DATA"); if (config.showSources && config.sourcesCount > 0) { if (config.showSources && !config.apiToken) { - Log.error( - this.name + - ": Can't load sources because the apiKey is not set.", - ); + Log.error(`${this.name}: Can't load sources because the apiKey is not set.`); } else { this.getPiholeData( config, { getQuerySources: config.sourcesCount }, - "PIHOLE_SOURCES", + "PIHOLE_SOURCES" ); } } } }, - isValidURL: function (url) { + isValidURL (url) { try { new URL(url); return true; @@ -45,11 +41,11 @@ module.exports = NodeHelper.create({ } }, - buildURL: function (config, params) { + buildURL (config, params) { params = params || {}; if (config.apiToken && !params.hasOwnProperty("auth")) { - params["auth"] = config.apiToken; + params.auth = config.apiToken; } const url = new URL(config.apiURL); @@ -58,22 +54,26 @@ module.exports = NodeHelper.create({ return url.toString(); }, - getPiholeData: function (config, params, notification) { + async getPiholeData (config, params, notification) { const self = this, url = self.buildURL(config, params), headers = { Referer: url }; this.sendSocketNotification("LOADING_PIHOLE_URL", url); - request({ url, headers, json: true }, (error, response, data) => { -Log.info('url', url) -//Log.info('response', response.statusCode) -//Log.info('data', data) - if (error) { - Log.error(self.name + " ERROR:", error); - } else { + try { + const response = await fetch(url, { headers }); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + if (response.headers.get('content-type').includes('application/json')) { + const data = await response.json(); self.sendSocketNotification(notification, data); + } else { + throw new Error(`Expected JSON but received ${response.headers.get('content-type')}`); } - }); + } catch (error) { + Log.error(self.name + " ERROR:", error); + } }, -}); +}); \ No newline at end of file diff --git a/package.json b/package.json index b14203e..6ca1358 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "MMMM-pihole-stats", + "name": "mmm-pihole-stats", "version": "1.0.0", - "description": "Pi-hole stats module for MagicMirror2", + "description": "Pi-hole stats module for MagicMirror².", "main": "MMM-pihole-stats.js", "repository": { "type": "git", @@ -17,8 +17,5 @@ "bugs": { "url": "https://github.com/sheyabernstein/MMM-pihole-stats/issues" }, - "homepage": "https://github.com/sheyabernstein/MMM-pihole-stats#readme", - "dependencies": { - "request": "^2.81.0" - } + "homepage": "https://github.com/sheyabernstein/MMM-pihole-stats#readme" }