2021-02-19 17:43:41 +00:00
|
|
|
// Import
|
2022-05-17 09:45:21 +00:00
|
|
|
const { Atk, Gio, GLib, GObject, Gtk, Soup, St } = imports.gi;
|
|
|
|
const { main, panelMenu, popupMenu } = imports.ui;
|
2021-02-19 17:43:41 +00:00
|
|
|
const ExtensionUtils = imports.misc.extensionUtils
|
|
|
|
const Lang = imports.lang;
|
|
|
|
const Mainloop = imports.mainloop;
|
|
|
|
|
|
|
|
const IndicatorName = 'pi-hole';
|
|
|
|
|
|
|
|
|
|
|
|
// Global storage
|
|
|
|
let PiHoleExt = {
|
|
|
|
// Extension metadata
|
2021-02-23 17:34:48 +00:00
|
|
|
Metadata : ExtensionUtils.getCurrentExtension(),
|
|
|
|
|
|
|
|
// Extension settings
|
|
|
|
Settings : null,
|
2021-02-19 17:43:41 +00:00
|
|
|
|
|
|
|
// The button
|
|
|
|
Button : null
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2021-02-23 17:34:48 +00:00
|
|
|
// Common
|
|
|
|
const Common = PiHoleExt.Metadata.imports.common;
|
2021-02-28 14:20:11 +00:00
|
|
|
const Gettext = imports.gettext.domain( PiHoleExt.Metadata.metadata['gettext-domain'] );
|
|
|
|
const _ = Gettext.gettext;
|
2021-02-23 17:34:48 +00:00
|
|
|
|
|
|
|
|
2021-02-19 17:43:41 +00:00
|
|
|
// Implement MythTV class
|
2022-05-17 09:45:21 +00:00
|
|
|
const PiHole = GObject.registerClass(
|
|
|
|
class PiHole extends panelMenu.Button
|
2021-02-19 17:43:41 +00:00
|
|
|
{
|
2022-05-17 09:45:21 +00:00
|
|
|
// ctor
|
|
|
|
_init ()
|
|
|
|
{
|
|
|
|
// Core setup
|
|
|
|
super._init( null, IndicatorName );
|
|
|
|
Common.initTranslations( PiHoleExt.Metadata );
|
2021-02-19 17:43:41 +00:00
|
|
|
|
2022-05-17 09:45:21 +00:00
|
|
|
this.Name = IndicatorName;
|
|
|
|
this.Extends = panelMenu.Button;
|
2021-02-19 17:43:41 +00:00
|
|
|
|
2022-05-17 09:45:21 +00:00
|
|
|
// Debug
|
|
|
|
this.Debug= false;
|
2021-02-19 17:43:41 +00:00
|
|
|
|
2022-05-17 09:45:21 +00:00
|
|
|
// Status URL
|
|
|
|
this.Url= '';
|
2021-02-19 17:43:41 +00:00
|
|
|
|
2022-05-17 09:45:21 +00:00
|
|
|
// API key
|
|
|
|
this.ApiKey = '';
|
2021-02-19 17:43:41 +00:00
|
|
|
|
2022-05-17 09:45:21 +00:00
|
|
|
// Timer period (seconds)
|
|
|
|
this.UpdateTime = 0;
|
2021-02-19 17:43:41 +00:00
|
|
|
|
2022-05-17 09:45:21 +00:00
|
|
|
// Disable duration (seconds)
|
|
|
|
this.DisableTime = 0;
|
2021-02-19 17:43:41 +00:00
|
|
|
|
2022-05-17 09:45:21 +00:00
|
|
|
// Updates
|
|
|
|
this.StatusEvent= null;
|
2021-02-19 17:43:41 +00:00
|
|
|
|
2022-05-17 09:45:21 +00:00
|
|
|
// Current status
|
|
|
|
this.StatusField= null;
|
|
|
|
this.IconStatus= "";
|
|
|
|
this.Status= "unknown";
|
2021-02-19 17:43:41 +00:00
|
|
|
|
2022-05-17 09:45:21 +00:00
|
|
|
// Buttons
|
|
|
|
this.Icon= null;
|
|
|
|
this.PauseButton= null;
|
|
|
|
this.EnableDisableButton= null;
|
|
|
|
this.SettingsButton= null;
|
2021-02-23 17:34:48 +00:00
|
|
|
|
2022-05-17 09:45:21 +00:00
|
|
|
// Watch settings
|
|
|
|
this.SettingChangedHandlerIds= null;
|
2021-02-19 17:43:41 +00:00
|
|
|
|
2021-02-23 17:34:48 +00:00
|
|
|
// Settings
|
|
|
|
let settings = Common.getSettings( PiHoleExt.Metadata );
|
|
|
|
this.Url = settings.get_string( Common.URL_SETTING );
|
|
|
|
this.ApiKey = settings.get_string( Common.API_KEY_SETTING );
|
|
|
|
this.UpdateTime = settings.get_uint( Common.UPDATE_RATE_SETTING );
|
|
|
|
if (this.UpdateTime < 5)
|
|
|
|
this.UpdateTime = 5;
|
|
|
|
this.DisableTime = settings.get_uint( Common.DISABLE_TIME_SETTING );
|
|
|
|
if (this.DisableTime < 1)
|
|
|
|
this.DisableTime = 1;
|
|
|
|
PiHoleExt.Settings = settings;
|
|
|
|
|
|
|
|
// Diag
|
|
|
|
if (this.Debug)
|
2021-02-19 17:43:41 +00:00
|
|
|
{
|
2021-02-23 17:34:48 +00:00
|
|
|
this.dprint("Url: " + this.Url);
|
|
|
|
this.dprint("ApiKey: " + this.ApiKey);
|
|
|
|
this.dprint("UpdateTime: " + this.UpdateTime.toString());
|
|
|
|
this.dprint("DisableTime: " + this.DisableTime.toString());
|
2021-02-19 17:43:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create a Soup session with which to do requests
|
|
|
|
this.SoupSession = new Soup.SessionAsync();
|
|
|
|
if (Soup.Session.prototype.add_feature != null)
|
|
|
|
Soup.Session.prototype.add_feature.call(this.SoupSession, new Soup.ProxyResolverDefault());
|
|
|
|
|
|
|
|
// Create button/icon
|
|
|
|
this.Icon = new St.Icon({ style_class: 'system-status-icon' });
|
|
|
|
this.add_child( this.Icon );
|
|
|
|
this.setIcon();
|
|
|
|
|
|
|
|
|
|
|
|
// Prep. menu
|
2022-05-17 09:45:21 +00:00
|
|
|
if (main.panel._menus == undefined)
|
|
|
|
main.panel.menuManager.addMenu(this.menu);
|
2021-02-19 17:43:41 +00:00
|
|
|
else
|
2022-05-17 09:45:21 +00:00
|
|
|
main.panel._menus.addMenu(this.menu);
|
2021-02-19 17:43:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Add status popup
|
2021-02-23 17:34:48 +00:00
|
|
|
|
2021-02-19 17:43:41 +00:00
|
|
|
// .. status
|
|
|
|
let box = new St.BoxLayout({style_class:'pihole-heading-row'});
|
2021-02-28 14:20:11 +00:00
|
|
|
let label = new St.Label({style_class:'pihole-label', text:_("Pi-Hole Status") + ": "});
|
2021-02-19 17:43:41 +00:00
|
|
|
box.add_actor(label);
|
|
|
|
this.StatusField = new St.Label({text:this.Status});
|
|
|
|
box.add_actor(this.StatusField);
|
|
|
|
this.addMenuItem(box);
|
2021-02-23 17:34:48 +00:00
|
|
|
|
2021-02-19 17:43:41 +00:00
|
|
|
// .. sep
|
2022-05-17 09:45:21 +00:00
|
|
|
this.menu.addMenuItem(new popupMenu.PopupSeparatorMenuItem());
|
2021-02-23 17:34:48 +00:00
|
|
|
|
|
|
|
// .. control buttons
|
2022-05-17 09:45:21 +00:00
|
|
|
this.PauseButton = new popupMenu.PopupMenuItem(_("Pause temporarily"), {style_class:"pihole-indent"});
|
2021-02-19 17:43:41 +00:00
|
|
|
this.PauseButton.connect('activate', Lang.bind(this, function() {
|
|
|
|
this.onPauseButton();
|
|
|
|
return 0;
|
|
|
|
}));
|
|
|
|
this.menu.addMenuItem(this.PauseButton);
|
|
|
|
//
|
2022-05-17 09:45:21 +00:00
|
|
|
this.EnableDisableButton = new popupMenu.PopupMenuItem(_("Disable"), {style_class:"pihole-indent"});
|
2021-02-19 17:43:41 +00:00
|
|
|
this.EnableDisableButton.connect('activate', Lang.bind(this, function() {
|
|
|
|
this.onEnableDisableButton();
|
|
|
|
return 0;
|
|
|
|
}));
|
|
|
|
this.menu.addMenuItem(this.EnableDisableButton);
|
2021-02-23 17:34:48 +00:00
|
|
|
|
2021-02-19 17:43:41 +00:00
|
|
|
// .. sep
|
2022-05-17 09:45:21 +00:00
|
|
|
this.menu.addMenuItem(new popupMenu.PopupSeparatorMenuItem());
|
2021-02-23 17:34:48 +00:00
|
|
|
|
2021-02-19 17:43:41 +00:00
|
|
|
// .. settings
|
2022-05-17 09:45:21 +00:00
|
|
|
this.SettingsButton = new popupMenu.PopupMenuItem(_("Settings"), {style_class:"pihole-indent"});
|
2021-02-19 17:43:41 +00:00
|
|
|
this.SettingsButton.connect('activate', Lang.bind(this, function() {
|
|
|
|
this.onSettingsButton();
|
|
|
|
return 0;
|
|
|
|
}));
|
|
|
|
this.menu.addMenuItem(this.SettingsButton);
|
|
|
|
|
2021-02-23 17:34:48 +00:00
|
|
|
// Get initial status (starts timer for next)
|
2021-02-19 17:43:41 +00:00
|
|
|
this.getPiHoleStatus();
|
2021-02-23 17:34:48 +00:00
|
|
|
|
|
|
|
// Watch for settings changes
|
|
|
|
this.SettingChangedHandlerIds = [
|
|
|
|
PiHoleExt.Settings.connect("changed::" + Common.URL_SETTING, Lang.bind(this, function() {
|
|
|
|
PiHoleExt.Button.Url = PiHoleExt.Settings.get_string( Common.URL_SETTING );
|
|
|
|
})),
|
|
|
|
PiHoleExt.Settings.connect("changed::" + Common.API_KEY_SETTING, Lang.bind(this, function() {
|
|
|
|
PiHoleExt.Button.ApiKey = PiHoleExt.Settings.get_string( Common.API_KEY_SETTING);
|
|
|
|
})),
|
|
|
|
PiHoleExt.Settings.connect("changed::" + Common.UPDATE_RATE_SETTING, Lang.bind(this, function() {
|
|
|
|
PiHoleExt.Button.UpdateTime = PiHoleExt.Settings.get_uint( Common.UPDATE_RATE_SETTING );
|
|
|
|
})),
|
|
|
|
PiHoleExt.Settings.connect("changed::" + Common.DISABLE_TIME_SETTING, Lang.bind(this, function() {
|
|
|
|
PiHoleExt.Button.DisableTime = PiHoleExt.Settings.get_uint( Common.DISABLE_TIME_SETTING );
|
|
|
|
}))
|
|
|
|
];
|
2022-05-17 09:45:21 +00:00
|
|
|
}
|
2021-02-19 17:43:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Debug
|
2022-05-17 09:45:21 +00:00
|
|
|
dprint(msg)
|
2021-02-19 17:43:41 +00:00
|
|
|
{
|
|
|
|
if (this.Debug)
|
|
|
|
print("PiHole: " + msg);
|
2022-05-17 09:45:21 +00:00
|
|
|
}
|
2021-02-19 17:43:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Error
|
2022-05-17 09:45:21 +00:00
|
|
|
eprint(msg)
|
2021-02-19 17:43:41 +00:00
|
|
|
{
|
|
|
|
global.log("PiHole: " + msg);
|
2022-05-17 09:45:21 +00:00
|
|
|
}
|
2021-02-19 17:43:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Add an item to the “menu”
|
2022-05-17 09:45:21 +00:00
|
|
|
addMenuItem(item)
|
2021-02-19 17:43:41 +00:00
|
|
|
{
|
2022-05-17 09:45:21 +00:00
|
|
|
let menuitem = new popupMenu.PopupBaseMenuItem({ reactive:false });
|
2021-02-19 17:43:41 +00:00
|
|
|
menuitem.actor.add_actor( item );
|
|
|
|
this.menu.addMenuItem( menuitem );
|
2022-05-17 09:45:21 +00:00
|
|
|
}
|
2021-02-19 17:43:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Set correct icon
|
2022-05-17 09:45:21 +00:00
|
|
|
setIcon()
|
2021-02-19 17:43:41 +00:00
|
|
|
{
|
|
|
|
if (this.Status == this.IconStatus)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this.IconStatus = this.Status;
|
|
|
|
if (this.IconStatus == "enabled")
|
|
|
|
this.Icon.set_gicon( this.getCustomIcon('pi-hole-symbolic') );
|
|
|
|
else if (this.IconStatus == "disabled")
|
|
|
|
this.Icon.set_gicon( this.getCustomIcon('pi-hole-disabled-symbolic') );
|
|
|
|
else
|
|
|
|
this.Icon.set_gicon( this.getCustomIcon('pi-hole-unknown-symbolic') );
|
2022-05-17 09:45:21 +00:00
|
|
|
}
|
2021-02-19 17:43:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Get custom icon from theme or file
|
2022-05-17 09:45:21 +00:00
|
|
|
getCustomIcon(icon_name)
|
2021-02-19 17:43:41 +00:00
|
|
|
{
|
|
|
|
let icon_path = PiHoleExt.Metadata.dir.get_child('icons').get_child( icon_name + ".svg" ).get_path();
|
|
|
|
let theme = Gtk.IconTheme.get_default();
|
|
|
|
if (theme)
|
|
|
|
{
|
|
|
|
let theme_icon = theme.lookup_icon( icon_name, -1, 2 );
|
|
|
|
if (theme_icon)
|
|
|
|
icon_path = theme_icon.get_filename();
|
|
|
|
}
|
|
|
|
this.dprint("setting new icon from " + icon_path);
|
|
|
|
return Gio.FileIcon.new( Gio.File.new_for_path( icon_path ) );
|
2022-05-17 09:45:21 +00:00
|
|
|
}
|
2021-02-19 17:43:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Request pi-hole status
|
2022-05-17 09:45:21 +00:00
|
|
|
getPiHoleStatus()
|
2021-02-19 17:43:41 +00:00
|
|
|
{
|
|
|
|
this.dprint("getting pi-hole status");
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// Trigger request
|
|
|
|
let me = this;
|
2021-02-23 17:34:48 +00:00
|
|
|
let url = this.Url + "/api.php?status&auth=" + this.ApiKey;
|
2021-02-19 17:43:41 +00:00
|
|
|
let request = Soup.Message.new('GET', url);
|
|
|
|
this.SoupSession.queue_message(request, function(soup, message) {
|
|
|
|
if (message.status_code == 200)
|
2021-02-23 17:34:48 +00:00
|
|
|
{
|
2021-02-19 17:43:41 +00:00
|
|
|
me.processPiHoleStatus(request.response_body.data);
|
2021-02-23 17:34:48 +00:00
|
|
|
}
|
2021-02-19 17:43:41 +00:00
|
|
|
else
|
2021-02-23 17:34:48 +00:00
|
|
|
{
|
|
|
|
me.eprint("error retrieving status: " + message.status_code);
|
|
|
|
me.newPiHoleStatus("unknown");
|
|
|
|
}
|
2021-02-19 17:43:41 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Now do it again in a bit
|
|
|
|
this.StatusEvent = GLib.timeout_add_seconds(0, this.UpdateTime, Lang.bind(this, function() {
|
|
|
|
this.getPiHoleStatus();
|
|
|
|
return 0;
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
catch (err)
|
|
|
|
{
|
|
|
|
this.eprint("exception requesting status: " + err);
|
|
|
|
}
|
2022-05-17 09:45:21 +00:00
|
|
|
}
|
2021-02-19 17:43:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Pause pi-hole
|
2022-05-17 09:45:21 +00:00
|
|
|
onPauseButton()
|
2021-02-19 17:43:41 +00:00
|
|
|
{
|
|
|
|
// Do op
|
|
|
|
this.dprint("pausing pi-hole");
|
|
|
|
this.enableDisable( "disable=" + this.DisableTime.toString() );
|
|
|
|
|
|
|
|
// Now ask for status again a second after it should be re-enabled
|
|
|
|
Mainloop.source_remove(PiHoleExt.Button.StatusEvent);
|
|
|
|
this.StatusEvent = GLib.timeout_add_seconds(0, this.DisableTime + 1, Lang.bind(this, function() {
|
|
|
|
this.getPiHoleStatus();
|
|
|
|
return 0;
|
|
|
|
}));
|
2022-05-17 09:45:21 +00:00
|
|
|
}
|
2021-02-19 17:43:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Enable or disable pi-hole
|
2022-05-17 09:45:21 +00:00
|
|
|
onEnableDisableButton()
|
2021-02-19 17:43:41 +00:00
|
|
|
{
|
|
|
|
// Do correct op
|
|
|
|
let op;
|
|
|
|
if (this.Status == "enabled")
|
|
|
|
{
|
|
|
|
this.dprint("disabling pi-hole (currently " + this.Status + ")");
|
|
|
|
op = "disable";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.dprint("enabling pi-hole (currently " + this.Status + ")");
|
|
|
|
op = "enable";
|
|
|
|
}
|
|
|
|
this.enableDisable( op );
|
|
|
|
|
|
|
|
// Restart status request cycle since we just got an up-to-date status
|
|
|
|
Mainloop.source_remove(PiHoleExt.Button.StatusEvent);
|
|
|
|
this.StatusEvent = GLib.timeout_add_seconds(0, this.UpdateTime, Lang.bind(this, function() {
|
|
|
|
this.getPiHoleStatus();
|
|
|
|
return 0;
|
|
|
|
}));
|
2022-05-17 09:45:21 +00:00
|
|
|
}
|
2021-02-19 17:43:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Enable or disable pi-hole given op
|
2022-05-17 09:45:21 +00:00
|
|
|
enableDisable( op )
|
2021-02-19 17:43:41 +00:00
|
|
|
{
|
|
|
|
this.dprint("requesting " + op);
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// Trigger request
|
|
|
|
let me = this;
|
2021-02-23 17:34:48 +00:00
|
|
|
let url = this.Url + "/api.php?" + op + "&auth=" + this.ApiKey;
|
2021-02-19 17:43:41 +00:00
|
|
|
let request = Soup.Message.new('GET', url);
|
|
|
|
this.SoupSession.queue_message(request, function(soup, message) {
|
|
|
|
if (message.status_code == 200)
|
2021-02-23 17:34:48 +00:00
|
|
|
{
|
2021-02-19 17:43:41 +00:00
|
|
|
me.processPiHoleStatus(request.response_body.data);
|
2021-02-23 17:34:48 +00:00
|
|
|
}
|
2021-02-19 17:43:41 +00:00
|
|
|
else
|
2021-02-23 17:34:48 +00:00
|
|
|
{
|
|
|
|
me.eprint("error requesting disable: " + message.status_code);
|
|
|
|
me.newPiHoleStatus("unknown");
|
|
|
|
}
|
2021-02-19 17:43:41 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
catch (err)
|
|
|
|
{
|
|
|
|
this.eprint("exception requesting enable/disable: " + err);
|
2021-02-23 17:34:48 +00:00
|
|
|
this.newPiHoleStatus("unknown");
|
2021-02-19 17:43:41 +00:00
|
|
|
}
|
2022-05-17 09:45:21 +00:00
|
|
|
}
|
2021-02-19 17:43:41 +00:00
|
|
|
|
|
|
|
|
2021-02-23 17:34:48 +00:00
|
|
|
// Handle status
|
2022-05-17 09:45:21 +00:00
|
|
|
processPiHoleStatus(data)
|
2021-02-19 17:43:41 +00:00
|
|
|
{
|
|
|
|
this.dprint("processing status");
|
|
|
|
|
|
|
|
// Process JSON response status
|
|
|
|
this.Status = "unknown";
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// Process results string
|
|
|
|
var obj = JSON.parse( data.toString() );
|
2021-02-23 17:34:48 +00:00
|
|
|
this.newPiHoleStatus( obj.status );
|
2021-02-19 17:43:41 +00:00
|
|
|
}
|
|
|
|
catch (err)
|
|
|
|
{
|
|
|
|
this.eprint("exception processing status [" + data.toString() + "]: " + err);
|
2021-02-23 17:34:48 +00:00
|
|
|
this.newPiHoleStatus("unknown");
|
2021-02-19 17:43:41 +00:00
|
|
|
}
|
2022-05-17 09:45:21 +00:00
|
|
|
}
|
2021-02-23 17:34:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
// New status
|
2022-05-17 09:45:21 +00:00
|
|
|
newPiHoleStatus(newstatus)
|
2021-02-23 17:34:48 +00:00
|
|
|
{
|
|
|
|
if (newstatus === undefined)
|
|
|
|
newstatus = "undefined";
|
|
|
|
this.Status = newstatus;
|
2021-02-19 17:43:41 +00:00
|
|
|
|
|
|
|
// Update statuses
|
|
|
|
this.dprint("got status " + this.Status);
|
|
|
|
this.StatusField.set_text( this.Status );
|
|
|
|
this.setIcon();
|
|
|
|
if (this.Status == "enabled")
|
|
|
|
this.EnableDisableButton.label.set_text("Disable");
|
|
|
|
else
|
|
|
|
this.EnableDisableButton.label.set_text("Enable");
|
2022-05-17 09:45:21 +00:00
|
|
|
}
|
2021-02-23 17:34:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Open settings
|
2022-05-17 09:45:21 +00:00
|
|
|
onSettingsButton()
|
2021-02-23 17:34:48 +00:00
|
|
|
{
|
|
|
|
imports.misc.util.spawn(['gnome-extensions', 'prefs', PiHoleExt.Metadata.uuid]);
|
|
|
|
}
|
2022-05-17 09:45:21 +00:00
|
|
|
});
|
2021-02-19 17:43:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Setup
|
|
|
|
function init()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Turn on
|
|
|
|
function enable()
|
|
|
|
{
|
|
|
|
PiHoleExt.Button = new PiHole();
|
2022-05-17 09:45:21 +00:00
|
|
|
main.panel.addToStatusArea( IndicatorName, PiHoleExt.Button );
|
2021-02-19 17:43:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Turn off
|
|
|
|
function disable()
|
|
|
|
{
|
2021-02-23 17:34:48 +00:00
|
|
|
// Disconnects the setting listeners
|
|
|
|
for (let id in this.SettingChangedHandlerIds)
|
|
|
|
this._settings.disconnect(this.SettingChangedHandlerIds[id]);
|
|
|
|
this.SettingChangedHandlerIds = null;
|
|
|
|
|
|
|
|
// Finish off
|
2022-05-17 09:45:21 +00:00
|
|
|
mainloop.source_remove(PiHoleExt.Button.StatusEvent);
|
2021-02-19 17:43:41 +00:00
|
|
|
PiHoleExt.Button.destroy();
|
|
|
|
PiHoleExt.Button = null;
|
|
|
|
}
|