Last active
November 5, 2025 16:58
-
-
Save gmoz22/3daa61753f27562dd7af460cb8a12eb6 to your computer and use it in GitHub Desktop.
GL.iNet GoodCloud dynamic world map - Tampermonkey
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name GoodCloud Dynamic Map | |
| // @namespace https://www.steveinnovates.com | |
| // @version 2025-07-24 | |
| // @description Displays a dynamic map of your bounded devices | |
| // @author Steve Oziel | |
| // @match https://www.goodcloud.xyz/ | |
| // @icon https://www.google.com/s2/favicons?sz=64&domain=goodcloud.xyz | |
| // @grant none | |
| // @downloadURL https://gist.github.com/gmoz22/3daa61753f27562dd7af460cb8a12eb6 | |
| // @updateURL https://gist.githubusercontent.com/gmoz22/3daa61753f27562dd7af460cb8a12eb6/raw/tampermonkey-glinet-goodcloud-map.js | |
| // ==/UserScript== | |
| // (function() { | |
| (async () => { | |
| 'use strict'; | |
| // Inject Leaflet CSS | |
| const leafletCSS = document.createElement('link'); | |
| leafletCSS.rel = 'stylesheet'; | |
| leafletCSS.href = 'https://unpkg.com/[email protected]/dist/leaflet.css'; | |
| leafletCSS.integrity = 'sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY='; | |
| leafletCSS.crossOrigin = ''; | |
| document.head.appendChild(leafletCSS); | |
| // Inject Leaflet JS | |
| const leafletJS = document.createElement('script'); | |
| leafletJS.src = 'https://unpkg.com/[email protected]/dist/leaflet.js'; | |
| leafletJS.integrity = 'sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo='; | |
| leafletJS.crossOrigin = ''; | |
| document.head.appendChild(leafletJS); | |
| const colors = { | |
| online: "#00c8b5", | |
| offline: "#8c8c8c", | |
| unsynced: "#e04c7e", | |
| deactivated: "#f5a623", | |
| blue: "#2196f3", | |
| } | |
| var map; | |
| // Get devices | |
| async function getDevices() { | |
| const token = document.cookie.split("=")[1]; | |
| await fetch("https://api.goodcloud.xyz/cloud-api/cloud/v2/device?pageNum=1&pageSize=1000&groupId=&ssid=&mac=&ip=&version=&model=&network_mode=&buttonsh=false&Intermediate=&services=&firmware_path=&data_path=&status=&name_desc=none&modem_version=&name=&description=&version_date=&version_time=&fuzzyKey=&test=&productType=Router&fuzzy_query=", { | |
| "headers": { | |
| "accept": "application/json, text/plain, */*", | |
| "accept-language": "en-US,en;q=0.9,fr;q=0.8", | |
| "cache-control": "no-cache", | |
| "pragma": "no-cache", | |
| "priority": "u=1, i", | |
| "sec-ch-ua": "\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\", \"Google Chrome\";v=\"138\"", | |
| "sec-ch-ua-mobile": "?0", | |
| "sec-ch-ua-platform": "\"macOS\"", | |
| "sec-fetch-dest": "empty", | |
| "sec-fetch-mode": "cors", | |
| "sec-fetch-site": "same-site", | |
| token | |
| }, | |
| "referrer": "https://www.goodcloud.xyz/", | |
| "body": null, | |
| "method": "GET", | |
| "mode": "cors", | |
| "credentials": "omit" | |
| }) | |
| .then((response) => { | |
| const reader = response.body.getReader(); | |
| return new ReadableStream({ | |
| start(controller) { | |
| return pump(); | |
| function pump() { | |
| return reader.read().then(({ done, value }) => { | |
| if (done) { | |
| controller.close(); | |
| return; | |
| } | |
| controller.enqueue(value); | |
| return pump(); | |
| }); | |
| } | |
| }, | |
| }); | |
| }) | |
| .then((stream) => new Response(stream)) | |
| .then((response) => response.json()) | |
| .then((result) => { | |
| for (let x = 0; x < result.info.rows.length; x++) { | |
| let device = result.info.rows[x]; | |
| // console.log(device); | |
| L.circleMarker( | |
| [parseInt(device.lat), parseInt(device.lng)], | |
| { | |
| color: (device.status?colors.online:colors.offline), | |
| weight: 5, | |
| opacity: 0.5, | |
| fillOpacity: 0.8 | |
| } | |
| ) | |
| .addTo(map) | |
| .bindPopup(popupInfo(device)); | |
| } | |
| }) | |
| } | |
| // Pin info bubble | |
| function popupInfo(device) { | |
| let deviceInfo = "<div>" + (device.status ? "<span title='Online' style='cursor: default; color: "+colors.online+"; font-weight: bold; font-size: 1.3em;'>◉</span>" : "<span title='Offline' style='color: "+colors.offline+"; font-weight: bold;'>Offline</span> ") + " <a style='font-size: 1.3em; font-weight: bold; margin-bottom: 4px;' href='#/share-device/editDevice/"+device.id+"?from=deviceListPage'>" + device.name + "</a></div>" | |
| + "<br/><div><b>Model</b>: " + device.boardInfoModel + "</div>" | |
| + "<div><b>MAC</b>: " + device.mac + "</div>" | |
| + "<div><b>IP</b>: " + device.ip + "</div>" | |
| if (device.position) { | |
| deviceInfo += "<br/><b>Location</b>:<div style='padding-left: 8px;'>" + device.position + "</div>" | |
| } | |
| if (device.description) { | |
| deviceInfo += "<br/><b>Description</b>:<div style='margin-left: 8px;'>" + device.description.split("\n").join("<br/>") + "</div>" | |
| } | |
| return deviceInfo; | |
| } | |
| // Draw Map Leaflet map is loaded and the target div exists | |
| function initMap() { | |
| const targetDiv = document.querySelector('div.map-img'); | |
| if (!window.L || !targetDiv) { | |
| setTimeout(initMap, 100); | |
| return; | |
| } | |
| // Remove existing background image, set height and add ID property | |
| targetDiv.style.background = 'none'; | |
| targetDiv.style.height = '600px'; | |
| targetDiv.id = 'map'; | |
| // Initialize Leaflet map | |
| map = L.map('map') | |
| .setView([22, -22], 2) | |
| // .setMaxZoom(5) | |
| // Atrribution | |
| L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { | |
| maxZoom: 19, | |
| attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' | |
| }).addTo(map); | |
| // Get devices | |
| getDevices(); | |
| } | |
| // Display initial map | |
| initMap(); | |
| // Display map again when we navigate back to dashboard | |
| window.navigation.addEventListener("navigate", (event) => { | |
| const currentHash = event.destination.url.split("#")[1]; | |
| if (currentHash === "/dashboard/index") { | |
| initMap(); | |
| } | |
| }); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment