Merge branch 'main' of gitea:ezri/dotfiles
This commit is contained in:
commit
74b1584e0d
@ -19,12 +19,18 @@
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
&.side {
|
||||
&.left-side {
|
||||
margin: 10px;
|
||||
margin-top: 20px;
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
&.right-side {
|
||||
margin: 10px;
|
||||
margin-top: 20px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
&.bg {
|
||||
background-color: $bg0;
|
||||
padding: 10px;
|
||||
|
||||
93
.config/eww/eww.yuck
Normal file
93
.config/eww/eww.yuck
Normal file
@ -0,0 +1,93 @@
|
||||
;; -*-lisp-*- Include modules
|
||||
(include "./modules/workspaces.yuck")
|
||||
(include "./modules/clock.yuck")
|
||||
(include "./modules/system.yuck")
|
||||
(include "./modules/network.yuck")
|
||||
(include "./modules/volume.yuck")
|
||||
(include "./modules/aggietime.yuck")
|
||||
(include "./modules/timer.yuck")
|
||||
(include "./modules/mpris.yuck")
|
||||
|
||||
(defvar power--state "normal")
|
||||
|
||||
(include "./windows.yuck")
|
||||
|
||||
(defwindow desktop-leftbar [group]
|
||||
:geometry (geometry :width "100%"
|
||||
:height "36px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h"
|
||||
:class "bar root"
|
||||
(hostname-leftalign)
|
||||
''
|
||||
(ws-group-rightalign :workspace-group {group})))
|
||||
|
||||
(defwindow desktop-mainbar [group]
|
||||
:geometry (geometry :width "100%"
|
||||
:height "36px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h"
|
||||
:class "bar root"
|
||||
(ws-group-leftalign :workspace-group {group})
|
||||
(hostname-centeralign)
|
||||
(desktop-details-rightalign)))
|
||||
|
||||
(defwindow desktop-rightbar [group]
|
||||
:geometry (geometry :width "100%"
|
||||
:height "36px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h"
|
||||
:class "bar root"
|
||||
(ws-group-leftalign :workspace-group {group})
|
||||
''
|
||||
(hostname-rightalign)))
|
||||
|
||||
(defwindow sidebar [group side]
|
||||
:geometry (geometry :width "200px"
|
||||
:height "100%"
|
||||
:anchor "${side} center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(sidebar :orientation {side}))
|
||||
|
||||
|
||||
|
||||
;; (defwindow network-status
|
||||
;; :monitor 0
|
||||
;; :geometry (geometry :width "200px"
|
||||
;; :height "0px"
|
||||
;; :x "300px"
|
||||
;; :y "0px"
|
||||
;; :anchor "top right")
|
||||
;; :exclusive false
|
||||
;; :focusable false
|
||||
;; :stacking "overlay"
|
||||
;; (box :orientation "v"
|
||||
;; :class "bar root bg outline"
|
||||
;; :visible {network--show-details}
|
||||
;; (network-detail)))
|
||||
|
||||
|
||||
(defwindow laptopbar [group battery]
|
||||
:geometry (geometry :width "100%"
|
||||
:height "36px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h"
|
||||
:class "bar root"
|
||||
(ws-group-leftalign :workspace-group {group})
|
||||
(hostname-centeralign)
|
||||
(laptop-details-rightalign :battery {battery} )))
|
||||
|
||||
@ -1,74 +0,0 @@
|
||||
;; -*-lisp-*- Include modules
|
||||
(include "./modules/workspaces.yuck")
|
||||
(include "./modules/clock.yuck")
|
||||
(include "./modules/system.yuck")
|
||||
(include "./modules/network.yuck")
|
||||
(include "./modules/volume.yuck")
|
||||
(include "./modules/aggietime.yuck")
|
||||
(include "./modules/timer.yuck")
|
||||
(include "./modules/mpris.yuck")
|
||||
|
||||
(include "./windows.yuck")
|
||||
|
||||
(defwindow leftbar
|
||||
:monitor 2
|
||||
:geometry (geometry :width "100%"
|
||||
:height "36px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h"
|
||||
:class "bar root"
|
||||
(normandy-leftbar--left)
|
||||
"" ;;(normandy-leftbar--center)
|
||||
(normandy-leftbar--right)))
|
||||
|
||||
(defwindow rightbar
|
||||
:monitor 1
|
||||
:geometry (geometry :width "100%"
|
||||
:height "26px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h"
|
||||
:class "bar root"
|
||||
(normandy-rightbar--left)
|
||||
"" ;;(normandy-rightbar--center)
|
||||
(normandy-rightbar--right)))
|
||||
|
||||
(defwindow centerbar
|
||||
:monitor 0
|
||||
:geometry (geometry :width "100%"
|
||||
:height "26px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h"
|
||||
:class "bar root"
|
||||
(normandy-centerbar--left)
|
||||
(normandy-centerbar--center)
|
||||
(normandy-centerbar--right)))
|
||||
|
||||
(defwindow sidebar
|
||||
:monitor 2
|
||||
:geometry (geometry :width "210px"
|
||||
:height "1044px"
|
||||
:anchor "left bottom")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(normandy-sidebar))
|
||||
|
||||
(defwindow sidebar2
|
||||
:monitor 1
|
||||
:geometry (geometry :width "210px"
|
||||
:height "1044px"
|
||||
:anchor "right bottom")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(normandy-sidebar))
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
;; -*-lisp-*- Include modules
|
||||
(include "./modules/workspaces.yuck")
|
||||
(include "./modules/clock.yuck")
|
||||
(include "./modules/system.yuck")
|
||||
(include "./modules/network.yuck")
|
||||
(include "./modules/volume.yuck")
|
||||
(include "./modules/aggietime.yuck")
|
||||
(include "./modules/timer.yuck")
|
||||
(include "./modules/mpris.yuck")
|
||||
|
||||
(defvar power--state "normal")
|
||||
|
||||
(include "./windows.yuck")
|
||||
|
||||
(defwindow builtinbar
|
||||
:monitor 0
|
||||
:geometry (geometry :width "100%"
|
||||
:height "36px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h" :class "bar root ${power--state == 'critical' ? 'reservepower' : ''}"
|
||||
(rocinante-builtinbar--left)
|
||||
(rocinante-builtinbar--center)
|
||||
(rocinante-builtinbar--right)))
|
||||
|
||||
(defwindow externalbar
|
||||
:monitor 1
|
||||
:geometry (geometry :width "100%"
|
||||
:height "36px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h" :class "bar root ${power--state == 'critical' ? 'reservepower' : ''}"
|
||||
(rocinante-externalbar--left)
|
||||
(rocinante-externalbar--center)
|
||||
(rocinante-externalbar--right)))
|
||||
|
||||
@ -1,81 +0,0 @@
|
||||
;; -*-lisp-*- Include modules
|
||||
(include "./modules/workspaces.yuck")
|
||||
(include "./modules/clock.yuck")
|
||||
(include "./modules/system.yuck")
|
||||
(include "./modules/network.yuck")
|
||||
(include "./modules/volume.yuck")
|
||||
(include "./modules/aggietime.yuck")
|
||||
(include "./modules/timer.yuck")
|
||||
(include "./modules/mpris.yuck")
|
||||
|
||||
(include "./windows.yuck")
|
||||
|
||||
(defwindow leftbar
|
||||
:monitor 1
|
||||
:geometry (geometry :width "100%"
|
||||
:height "36px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h"
|
||||
:class "bar root"
|
||||
(tycho-leftbar--left)
|
||||
(tycho-leftbar--center)
|
||||
(tycho-leftbar--right)))
|
||||
|
||||
(defwindow centerbar
|
||||
:monitor 0
|
||||
:geometry (geometry :width "100%"
|
||||
:height "26px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h"
|
||||
:class "bar root"
|
||||
(tycho-centerbar--left)
|
||||
(tycho-centerbar--center)
|
||||
(tycho-centerbar--right)))
|
||||
|
||||
(defwindow rightbar
|
||||
:monitor 2
|
||||
:geometry (geometry :width "1440px"
|
||||
:height "36px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h"
|
||||
:class "bar root"
|
||||
(tycho-rightbar--left)
|
||||
(tycho-rightbar--center)
|
||||
(tycho-rightbar--right)))
|
||||
|
||||
(defwindow sidebar
|
||||
:monitor 1
|
||||
:geometry (geometry :width "200px"
|
||||
:height "100%"
|
||||
:anchor "left center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(tycho-sidebar))
|
||||
|
||||
|
||||
|
||||
(defwindow network-status
|
||||
:monitor 0
|
||||
:geometry (geometry :width "200px"
|
||||
:height "0px"
|
||||
:x "300px"
|
||||
:y "0px"
|
||||
:anchor "top right")
|
||||
:exclusive false
|
||||
:focusable false
|
||||
:stacking "overlay"
|
||||
(box :orientation "v"
|
||||
:class "bar root bg outline"
|
||||
:visible {network--show-details}
|
||||
(network-detail)))
|
||||
|
||||
@ -1,107 +0,0 @@
|
||||
;; -*-lisp-*- Include modules
|
||||
(include "./modules/workspaces.yuck")
|
||||
(include "./modules/clock.yuck")
|
||||
(include "./modules/system.yuck")
|
||||
(include "./modules/network.yuck")
|
||||
(include "./modules/volume.yuck")
|
||||
(include "./modules/aggietime.yuck")
|
||||
(include "./modules/timer.yuck")
|
||||
(include "./modules/mpris.yuck")
|
||||
|
||||
(defvar power--state "normal")
|
||||
|
||||
(include "./windows.yuck")
|
||||
|
||||
(defwindow leftbar
|
||||
:monitor 1
|
||||
:geometry (geometry :width "100%"
|
||||
:height "36px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h"
|
||||
:class "bar root"
|
||||
(tycho-leftbar--left)
|
||||
(tycho-leftbar--center)
|
||||
(tycho-leftbar--right)))
|
||||
|
||||
(defwindow centerbar
|
||||
:monitor 0
|
||||
:geometry (geometry :width "100%"
|
||||
:height "26px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h"
|
||||
:class "bar root"
|
||||
(tycho-centerbar--left)
|
||||
(tycho-centerbar--center)
|
||||
(tycho-centerbar--right)))
|
||||
|
||||
(defwindow rightbar
|
||||
:monitor 2
|
||||
:geometry (geometry :width "100%"
|
||||
:height "36px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h"
|
||||
:class "bar root"
|
||||
(tycho-rightbar--left)
|
||||
(tycho-rightbar--center)
|
||||
(tycho-rightbar--right)))
|
||||
|
||||
(defwindow sidebar
|
||||
:monitor 1
|
||||
:geometry (geometry :width "200px"
|
||||
:height "100%"
|
||||
:anchor "left center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(tycho-sidebar))
|
||||
|
||||
|
||||
|
||||
(defwindow network-status
|
||||
:monitor 0
|
||||
:geometry (geometry :width "200px"
|
||||
:height "0px"
|
||||
:x "300px"
|
||||
:y "0px"
|
||||
:anchor "top right")
|
||||
:exclusive false
|
||||
:focusable false
|
||||
:stacking "overlay"
|
||||
(box :orientation "v"
|
||||
:class "bar root bg outline"
|
||||
:visible {network--show-details}
|
||||
(network-detail)))
|
||||
|
||||
|
||||
(defwindow builtinbar
|
||||
:monitor 0
|
||||
:geometry (geometry :width "100%"
|
||||
:height "36px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h" :class "bar root ${power--state == 'critical' ? 'reservepower' : ''}"
|
||||
(rocinante-builtinbar--left)
|
||||
(rocinante-builtinbar--center)
|
||||
(rocinante-builtinbar--right)))
|
||||
|
||||
|
||||
(defwindow polkit
|
||||
:geometry (geometry
|
||||
:anchor "center")
|
||||
:focusable true
|
||||
:exclusive false
|
||||
:stacking "overlay"
|
||||
(box :orientation "v"
|
||||
:class "bar root bg outline"
|
||||
"testing"))
|
||||
@ -1,194 +0,0 @@
|
||||
(deflisten network--data
|
||||
`~/.config/eww/scripts/network.py`)
|
||||
|
||||
(defwidget network--wlan [device]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class "offline"
|
||||
:visible {network--data["network"][device]["offline"]}
|
||||
:text "offline")
|
||||
(label :visible {network--data["network"][device]["connecting"]}
|
||||
:class "highlight"
|
||||
:text "connecting...")
|
||||
(label :visible {network--data["network"][device]["online"]}
|
||||
:class "special"
|
||||
:text "${network--data['wifi']['ssid']}")))
|
||||
|
||||
(defwidget network--lan [device]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class "offline"
|
||||
:visible {network--data["network"][device]["offline"]}
|
||||
:text "offline")
|
||||
(label :visible {network--data["network"][device]["connecting"]}
|
||||
:class "highlight"
|
||||
:text "connecting...")
|
||||
(label :visible {network--data["network"][device]["online"] && !network--data.network[device].connecting}
|
||||
:class "special"
|
||||
:text "${network--data['network'][device]['ip4_addr']}/${network--data['network'][device]['ip4_prefix']}")))
|
||||
|
||||
(defwidget network--proxy-vpn [device ?required]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
:visible {network--data["network"][device]["exists"] || required}
|
||||
(label :class "highlight"
|
||||
:text "- insecure"
|
||||
:visible {! network--data["network"][device]["exists"]})
|
||||
(label :class "green"
|
||||
:text "- secured"
|
||||
:visible {network--data["network"][device]["exists"]})))
|
||||
|
||||
(defwidget proxy-network [device]
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class "green"
|
||||
:text "connected"
|
||||
:visible {network--data.network[device].exists})
|
||||
(label :class "offline"
|
||||
:text "offline"
|
||||
:visible {!network--data.network[device].exists})
|
||||
"proxy vpn"))
|
||||
|
||||
(defwidget vpn-network []
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class "highlight"
|
||||
:text "offline"
|
||||
:visible {! network--data["network"]["ezrinet"]["exists"] || ! network--data["connected"]})
|
||||
(label :class "green"
|
||||
:text "connected"
|
||||
:visible {network--data["network"]["ezrinet"]["exists"] && network--data["connected"]})
|
||||
"personal network"))
|
||||
|
||||
(defwidget network []
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(box :orientation "h"
|
||||
:halign "center"
|
||||
:space-evenly false
|
||||
:spacing 10
|
||||
(network--lan :device "insight")
|
||||
(network--proxy-vpn :device "wg-mullvad" :required {!network--data.trusted}))
|
||||
"communications"))
|
||||
|
||||
(defwidget network-sidebar-details []
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:class "module"
|
||||
:space-evenly false
|
||||
:spacing 5
|
||||
:width 200
|
||||
(box :orientation "h"
|
||||
:halign "center"
|
||||
:class "nebula"
|
||||
:spacing 10
|
||||
:space-evenly false
|
||||
(label :text "Comms"
|
||||
:class "medium special"))
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:class "nebula"
|
||||
:spacing 10
|
||||
:width 200
|
||||
:space-evenly false
|
||||
(box :halign "start"
|
||||
"Status:")
|
||||
""
|
||||
(box :halign "end"
|
||||
(label :text "Online"
|
||||
:class "green"
|
||||
:visible {network--data.connected})
|
||||
(label :text "Offline"
|
||||
:class "highlight"
|
||||
:visible {!network--data.connected})))
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:class "nebula"
|
||||
:spacing 10
|
||||
:width 200
|
||||
:space-evenly false
|
||||
(box :halign "start"
|
||||
"VPN:")
|
||||
""
|
||||
(box :halign "end"
|
||||
(label :text "Connected"
|
||||
:class "green"
|
||||
:visible {network--data.network.ezrinet.online && network--data.connected})
|
||||
(label :text "Offline"
|
||||
:class "highlight"
|
||||
:visible {!network--data.network.ezrinet.exists})))
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:class "nebula"
|
||||
:spacing 10
|
||||
:width 200
|
||||
:space-evenly false
|
||||
(box :halign "start"
|
||||
"Proxy:")
|
||||
""
|
||||
(box :halign "end"
|
||||
(label :text "Connected"
|
||||
:class "green"
|
||||
:visible {network--data.network.wg-mullvad.online})
|
||||
(label :text "Offline"
|
||||
:class "highlight"
|
||||
:visible {!network--data.network.wg-mullvad.exists})))
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:class "nebula"
|
||||
:spacing 10
|
||||
:width 200
|
||||
:space-evenly false
|
||||
(box :halign "start"
|
||||
"Wi-Fi:")
|
||||
""
|
||||
(box :halign "end"
|
||||
(label :text {network--data.wifi.ssid}
|
||||
:limit-width 12
|
||||
:class "green"
|
||||
:visible {network--data.wifi.connected})
|
||||
(label :text "Offline"
|
||||
:class "offline"
|
||||
:visible {!network--data.wifi.connected})))
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:spacing 10
|
||||
:width 200
|
||||
:class "nebula"
|
||||
:space-evenly false
|
||||
(box :valign "start"
|
||||
:halign "start"
|
||||
"Addrs:")
|
||||
""
|
||||
(box :valign "start"
|
||||
:halign "end"
|
||||
:visible {network--data.connected}
|
||||
:orientation "v"
|
||||
(for addr in {network--data.ip4_addrs}
|
||||
(box :class "special"
|
||||
:halign "end"
|
||||
{addr}))
|
||||
(box :visible {!network--data.connected}
|
||||
:class "offline"
|
||||
"none")))
|
||||
;; (box :orientation "h"
|
||||
;; :halign "start"
|
||||
;; :class "nebula"
|
||||
;; :spacing 10
|
||||
;; :space-evenly false
|
||||
;; (label :text "Addr:")
|
||||
;; (label :text {network--data.network[device].ip4_addr}
|
||||
;; :class "medium special"))))
|
||||
))
|
||||
@ -1,77 +0,0 @@
|
||||
;; -*-lisp-*-
|
||||
(deflisten network--data
|
||||
`~/.config/eww/scripts/network.py`)
|
||||
|
||||
(defwidget network--wlan [device]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 10
|
||||
(label :class "offline"
|
||||
:visible {network--data["network"][device]["offline"]}
|
||||
:text "offline")
|
||||
(label :visible {network--data["network"][device]["connecting"]}
|
||||
:class "highlight"
|
||||
:text "connecting...")
|
||||
(label :visible {network--data["network"][device]["online"] && !network--data.network[device].connecting}
|
||||
:class "special"
|
||||
:text "${network--data['wifi']['ssid']}")
|
||||
(label :visible {!network--data.connection.internet && network--data.network[device].online}
|
||||
:class "highlight"
|
||||
:text "- no internet"
|
||||
)))
|
||||
|
||||
(defwidget network--lan [device]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class "offline"
|
||||
:visible {network--data["network"][device]["offline"]}
|
||||
:text "offline")
|
||||
(label :visible {network--data["network"][device]["connecting"]}
|
||||
:class "highlight"
|
||||
:text "connecting...")
|
||||
(label :visible {network--data["network"][device]["online"] && !network--data.network[device].connecting}
|
||||
:class "special"
|
||||
:text "${network--data['network'][device]['ip4_addr']}/${network--data['network'][device]['ip4_prefix']}")))
|
||||
|
||||
(defwidget network--proxy-vpn [device ?required]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
:visible {network--data.connection.internet && (network--data["network"][device]["exists"] || required)}
|
||||
(label :class "highlight"
|
||||
:text "- insecure"
|
||||
:visible {! network--data["network"][device]["exists"]})
|
||||
(label :class "green"
|
||||
:text "- secured"
|
||||
:visible {network--data["network"][device]["exists"]})))
|
||||
|
||||
(defwidget vpn-network []
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class "highlight"
|
||||
:text "offline"
|
||||
:visible {!network--data.connection.ezrinet})
|
||||
(label :class "green"
|
||||
:text "connected"
|
||||
:visible {network--data.connection.ezrinet})
|
||||
"personal network"))
|
||||
|
||||
(defwidget network []
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(box :orientation "h"
|
||||
:halign "center"
|
||||
:space-evenly false
|
||||
:spacing 10
|
||||
(network--wlan :device "wlan0")
|
||||
(network--proxy-vpn :device "wg-mullvad" :required {!network--data.trusted}))
|
||||
"communications"))
|
||||
|
||||
@ -1,347 +0,0 @@
|
||||
;; -*-lisp-*-
|
||||
(deflisten network--data
|
||||
`~/.config/eww/scripts/network.py`)
|
||||
|
||||
(defvar network--show-details false)
|
||||
|
||||
(defwidget network--interface
|
||||
[device]
|
||||
(box :orientation "v"
|
||||
:halign "center"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class "offline"
|
||||
:visible {!device.online}
|
||||
:text "offline")
|
||||
(label :visible {device.online}
|
||||
:class "special"
|
||||
:text "${device.addresses[0].address}/${device.addresses[0].prefixlen}")))
|
||||
|
||||
(defwidget network-big--online
|
||||
[]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:valign "center"
|
||||
:space-evenly false
|
||||
:spacing 10
|
||||
(label :class "medium green nebula"
|
||||
:visible {network--data.online}
|
||||
:text "online")
|
||||
(label :class "medium offline nebula"
|
||||
:visible {network--data.offline}
|
||||
:text "offline")
|
||||
(label :class "medium highlight nebula"
|
||||
:visible {network--data.configuring}
|
||||
:text "configuring")))
|
||||
|
||||
(defwidget network-big--lan
|
||||
[device]
|
||||
(label :visible {network--data
|
||||
[device]
|
||||
.online}
|
||||
:class ""
|
||||
:text "${network--data.interfaces[device].addresses[0].address}/${network--data.interfaces[device].addresses[0].prefixlen}"))
|
||||
|
||||
(defwidget network--wlan
|
||||
[device]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class "offline"
|
||||
:visible {network--data
|
||||
["network"]
|
||||
[device]
|
||||
["offline"]
|
||||
}
|
||||
:text "offline")
|
||||
(label :visible {network--data
|
||||
["network"]
|
||||
[device]
|
||||
["connecting"]
|
||||
}
|
||||
:class "highlight"
|
||||
:text "connecting...")
|
||||
(label :visible {network--data
|
||||
["network"]
|
||||
[device]
|
||||
["online"]
|
||||
}
|
||||
:class "special"
|
||||
:text "${network--data['wifi']['ssid']}")))
|
||||
|
||||
(defwidget network--lan
|
||||
[device]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class "offline"
|
||||
:visible {!device.online}
|
||||
:text "offline")
|
||||
(label :visible {device.online}
|
||||
:class {network--data.last_update.unix < clock--data.unix - 30 ? "highlight" : "special"}
|
||||
:text "${device.addresses[0].address}/${device.addresses[0].prefixlen}")))
|
||||
|
||||
(defwidget network--secure
|
||||
[]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class "highlight"
|
||||
:text "- insecure"
|
||||
:visible {!
|
||||
network--data.secure })
|
||||
(label :class "green"
|
||||
:text "- secured via ${network--data.secure_msg}"
|
||||
:visible {network--data.secure})))
|
||||
|
||||
(defwidget vpn-network
|
||||
[]
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
:visible {network--data
|
||||
!=
|
||||
''}
|
||||
(label :class "highlight"
|
||||
:text "offline"
|
||||
:visible {!
|
||||
network--data.interfaces.ezrinet.online })
|
||||
(label :class "green"
|
||||
:text "connected"
|
||||
:visible {network--data.interfaces.ezrinet.online})
|
||||
"personal network"))
|
||||
|
||||
(defwidget network
|
||||
[]
|
||||
(button :onclick "eww update network--show-details=${!network--show-details}"
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
:visible {network--data != ''}
|
||||
(box :orientation "h"
|
||||
:halign "center"
|
||||
:space-evenly false
|
||||
:spacing 10
|
||||
(network--lan :device {network--data.default_interface}))
|
||||
"communications")))
|
||||
|
||||
(defwidget network--public-ip
|
||||
[]
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:width 200
|
||||
(box :halign "start"
|
||||
"public ip:")
|
||||
""
|
||||
(box :halign "end"
|
||||
:spacing 0
|
||||
:space-evenly false
|
||||
(label :class "highlight"
|
||||
:text "offline"
|
||||
:visible {!network--data.online})
|
||||
(label :class "highlight"
|
||||
:text "no response"
|
||||
:visible {network--data.online && !network--data.have_public_ip})
|
||||
(label :class "special"
|
||||
:text {network--data.public_ip.ip}
|
||||
:visible {network--data.online && network--data.have_public_ip}))))
|
||||
|
||||
(defwidget network--default-route
|
||||
[]
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:width 200
|
||||
(box :halign "start"
|
||||
"route on:")
|
||||
""
|
||||
(box :halign "end"
|
||||
:spacing 0
|
||||
:space-evenly false
|
||||
(label :class "highlight"
|
||||
:text "offline"
|
||||
:visible {!network--data.online})
|
||||
(label :class "highlight"
|
||||
:text "no route"
|
||||
:visible {network--data.online && !network--data.have_default_route})
|
||||
(label :class "special"
|
||||
:text {network--data.default_route}
|
||||
:visible {network--data.online && network--data.have_default_route}))))
|
||||
|
||||
(defwidget network--gateway
|
||||
[]
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:width 200
|
||||
(box :halign "start"
|
||||
"gateway:")
|
||||
""
|
||||
(box :halign "end"
|
||||
:spacing 0
|
||||
:space-evenly false
|
||||
(label :class "highlight"
|
||||
:text "no response"
|
||||
:visible {!network--data.have_gateway})
|
||||
(label :class "special"
|
||||
:text {network--data.gateway}
|
||||
:visible {network--data.have_gateway}))))
|
||||
|
||||
(defwidget network--status-summary
|
||||
[]
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:width 200
|
||||
(box :halign "start"
|
||||
"status:")
|
||||
""
|
||||
(box :halign "end"
|
||||
:spacing 0
|
||||
:space-evenly false
|
||||
(label :class "highlight"
|
||||
:text "offline"
|
||||
:visible {!network--data.online})
|
||||
(label :class "special"
|
||||
:text "online"
|
||||
:visible {network--data.online && !network--data.secure})
|
||||
(label :class "green"
|
||||
:text {network--data.secure_msg == "usu" ? "vpn online" : "secure"}
|
||||
:visible {network--data.online && network--data.secure}))))
|
||||
|
||||
|
||||
(defwidget network-detail
|
||||
[]
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(network--status-summary)
|
||||
(network--public-ip)
|
||||
(network--default-route)
|
||||
(network--gateway)
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:width 200
|
||||
(box :halign "start"
|
||||
"last update:")
|
||||
""
|
||||
(box :halign "end"
|
||||
:spacing 0
|
||||
:space-evenly false
|
||||
(label :class {network--data.last_update.unix < clock--data.unix - 30 ? "highlight" : "special"}
|
||||
:text "${network--data.last_update.month}-${network--data.last_update.day} ${network--data.last_update.hour}:${network--data.last_update.minute}:${network--data.last_update.second}")))
|
||||
))
|
||||
|
||||
(defwidget network-sidebar-details []
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:class "module"
|
||||
:space-evenly false
|
||||
:spacing 5
|
||||
:width 200
|
||||
(box :orientation "h"
|
||||
:halign "center"
|
||||
:class "nebula"
|
||||
:spacing 10
|
||||
:space-evenly false
|
||||
(label :class "medium special"
|
||||
:text "Comms"))
|
||||
(box :orientation "h"
|
||||
:halign "center"
|
||||
:class "nebula"
|
||||
:spacing 10
|
||||
:space-evenly false
|
||||
(label :class "nebula green"
|
||||
:text "Online"
|
||||
:visible {network--data.online})
|
||||
(label :class "nebula highlight"
|
||||
:text "Offline"
|
||||
:visible {!network--data.online}))
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:spacing 10
|
||||
:width 200
|
||||
:space-evenly false
|
||||
(box :halign "start"
|
||||
:class "nebula"
|
||||
"Route On:")
|
||||
""
|
||||
(box :halign "end"
|
||||
(label :text "No Route"
|
||||
:class "highlight"
|
||||
:visible {network--data.online && !network--data.have_default_route})
|
||||
(label :text "${network--data.default_route}"
|
||||
:class "special"
|
||||
:visible {network--data.online && network--data.have_default_route})))
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:spacing 10
|
||||
:width 200
|
||||
:space-evenly false
|
||||
(box :halign "start"
|
||||
:valign "start"
|
||||
:class "nebula"
|
||||
"Address:")
|
||||
""
|
||||
(box :halign "end"
|
||||
:orientation "v"
|
||||
(label :text "${network--data.default_interface.addresses[0].address}"
|
||||
:class "special")
|
||||
(label :text "/${network--data.default_interface.addresses[0].prefixlen}"
|
||||
:halign "end"
|
||||
:class "special")))
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:spacing 10
|
||||
:width 200
|
||||
:space-evenly false
|
||||
(box :halign "start"
|
||||
:class "nebula"
|
||||
"Public IP:")
|
||||
""
|
||||
(box :halign "end"
|
||||
(label :text "Offline"
|
||||
:class "highlight"
|
||||
:visible {!network--data.have_public_ip})
|
||||
(label :text {network--data.public_ip.ip}
|
||||
:class "special"
|
||||
:visible {network--data.have_public_ip})))
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:spacing 10
|
||||
:width 200
|
||||
:space-evenly false
|
||||
(box :halign "start"
|
||||
:class "nebula"
|
||||
"Gateway:")
|
||||
""
|
||||
(box :halign "end"
|
||||
(label :text "Error"
|
||||
:class "highlight nebula"
|
||||
:visible {!network--data.have_gateway})
|
||||
(label :text {network--data.gateway}
|
||||
:class "special"
|
||||
:visible {network--data.have_gateway})))
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:class "nebula"
|
||||
:spacing 10
|
||||
:width 200
|
||||
:space-evenly false
|
||||
(box :halign "start"
|
||||
"VPN:")
|
||||
""
|
||||
(box :halign "end"
|
||||
(label :text "Offline"
|
||||
:class "highlight"
|
||||
:visible {!network--data.interfaces.ezrinet.online})
|
||||
(label :text "Online"
|
||||
:class "green"
|
||||
:visible {network--data.interfaces.ezrinet.online})))
|
||||
|
||||
))
|
||||
|
||||
|
||||
@ -27,3 +27,54 @@
|
||||
(label :class {volume--data.input.mute ? "offline" : "special"}
|
||||
:text "${volume--data.input.volume}%"))
|
||||
"audio system"))
|
||||
|
||||
(defwidget volume--gauge [io]
|
||||
(box :orientation "v"
|
||||
:halign {io == "in" ? "start" : "end"}
|
||||
:width 95
|
||||
:space-evenly false
|
||||
:spacing 10
|
||||
:class "nebula"
|
||||
(label :text {io})
|
||||
(box :halign "center"
|
||||
(overlay
|
||||
(system--gauge :value {volume--data["${io}put"].volume}
|
||||
:threshold {volume--data["${io}put"].mute ? 0 : 100})
|
||||
(transform :translate-y "-2px"
|
||||
(box
|
||||
(box :halign "center"
|
||||
:valign "center"
|
||||
:orientation "h"
|
||||
:space-evenly false
|
||||
:visible {!volume--data["${io}put"].mute}
|
||||
(label :text "%"
|
||||
:class "invisible")
|
||||
(label :text {round(volume--data["${io}put"].volume, 0)}
|
||||
:class "special")
|
||||
(label :text "%"
|
||||
:class "offline"))
|
||||
(box :halign "center"
|
||||
:valign "center"
|
||||
:orientation "h"
|
||||
:space-evenly false
|
||||
:visible {volume--data["${io}put"].mute}
|
||||
(label :text "mute"
|
||||
:class "offline"))
|
||||
))))))
|
||||
|
||||
(defwidget volume-gauges []
|
||||
(box :orientation "v"
|
||||
:space-evenly false
|
||||
:spacing 10
|
||||
:width 200
|
||||
:halign "start"
|
||||
:class "nebula"
|
||||
(label :class "medium special"
|
||||
:text "Audio")
|
||||
(box :orientation "h"
|
||||
:space-evenly false
|
||||
:width 200
|
||||
:halign "start"
|
||||
:spacing 10
|
||||
(volume--gauge :io "out")
|
||||
(volume--gauge :io "in"))))
|
||||
|
||||
@ -1,64 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
import sys
|
||||
from time import sleep
|
||||
|
||||
TRUSTED_NETWORKS = ['honnouji', 'honnouji_2.4']
|
||||
|
||||
def wifi():
|
||||
ssid_cmd = subprocess.run(['iwgetid', '-r'], capture_output = True)
|
||||
if ssid_cmd.returncode != 0:
|
||||
return {"connected": False, "ssid": None}
|
||||
return {"connected": True, "ssid": ssid_cmd.stdout.decode('utf-8').strip()}
|
||||
|
||||
def netdev(device: str):
|
||||
ip_cmd = subprocess.run(['ip', '-j', 'addr', 'show', device], capture_output = True)
|
||||
if ip_cmd.returncode != 0:
|
||||
sys.stderr.write(ip_cmd.stdout.decode('utf-8'))
|
||||
return {"exists": False, "online": False}
|
||||
ip_data = json.loads(ip_cmd.stdout.decode('utf-8'))[0]
|
||||
ip4_addr = ""
|
||||
ip4_prefix_length = 24
|
||||
addr4_info = list(filter(lambda addr: addr.get("family") == "inet", ip_data["addr_info"]))
|
||||
if len(addr4_info) >= 1:
|
||||
ip4_addr = addr4_info[0]["local"]
|
||||
ip4_prefix_length = addr4_info[0]["prefixlen"]
|
||||
online = ip_data["operstate"] == "UP" or ip4_addr != ""
|
||||
connecting = ip_data["operstate"] != "DOWN" and (ip4_addr == "" or not online)
|
||||
return {
|
||||
"exists": True,
|
||||
"online": online,
|
||||
"connecting": connecting,
|
||||
"offline": not online and not connecting,
|
||||
"ip4_addr": ip4_addr,
|
||||
"ip4_prefix": ip4_prefix_length
|
||||
}
|
||||
|
||||
def trusted(ssid: str | None):
|
||||
# Don't throw up an "INSECURE" alert when offline
|
||||
if not ssid:
|
||||
return True
|
||||
return ssid in TRUSTED_NETWORKS
|
||||
|
||||
while True:
|
||||
insight = netdev('insight')
|
||||
wlan0 = netdev('wlan0')
|
||||
wifi_data = wifi()
|
||||
connected = insight['exists'] and insight['online'] or wifi_data['connected']
|
||||
networks = {
|
||||
"insight": insight,
|
||||
"wlan0": netdev("wlan0"),
|
||||
"ezrinet": netdev("ezrinet"),
|
||||
"wg-mullvad": netdev("wg-mullvad"),
|
||||
}
|
||||
result = {
|
||||
'connected': connected,
|
||||
"network": networks,
|
||||
'wifi': wifi_data,
|
||||
"trusted": True,
|
||||
"ip4_addrs": [ network.get('ip4_addr') for network in networks.values() if network.get('ip4_addr', "") != "" ]
|
||||
}
|
||||
print(json.dumps(result), flush=True)
|
||||
sleep(1)
|
||||
@ -1,81 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
from time import sleep
|
||||
|
||||
TRUSTED_NETWORKS = ['gayer people']
|
||||
|
||||
def wifi():
|
||||
ssid_cmd = subprocess.run(['iwgetid', '-r'], capture_output = True)
|
||||
if ssid_cmd.returncode != 0:
|
||||
return {"connected": False, "ssid": None}
|
||||
ssid = ssid_cmd.stdout.decode('utf-8').strip()
|
||||
ssid = ssid if len(ssid) <= 15 else f"{ssid[:14]}…"
|
||||
return {"connected": True, "ssid": ssid}
|
||||
|
||||
def netdev(device: str):
|
||||
ip_cmd = subprocess.run(['ip', '-j', 'addr', 'show', device], capture_output = True)
|
||||
if ip_cmd.returncode != 0:
|
||||
return {"exists": False}
|
||||
ip_data = json.loads(ip_cmd.stdout.decode('utf-8'))[0]
|
||||
online = ip_data["operstate"] == "UP"
|
||||
ip4_addr = ""
|
||||
ip4_prefix_length = 24
|
||||
addr4_info = list(filter(lambda addr: addr.get("family") == "inet", ip_data["addr_info"]))
|
||||
if len(addr4_info) >= 1:
|
||||
ip4_addr = addr4_info[0]["local"]
|
||||
ip4_prefix_length = addr4_info[0]["prefixlen"]
|
||||
connecting = ip_data["operstate"] != "DOWN" and (ip4_addr == "" or not online)
|
||||
return {
|
||||
"exists": True,
|
||||
"online": online,
|
||||
"connecting": connecting,
|
||||
"offline": not online and not connecting,
|
||||
"ip4_addr": ip4_addr,
|
||||
"ip4_prefix": ip4_prefix_length
|
||||
}
|
||||
|
||||
def trusted(ssid: str | None):
|
||||
# Don't throw up an "INSECURE" alert when offline
|
||||
if not ssid:
|
||||
return True
|
||||
return ssid in TRUSTED_NETWORKS
|
||||
|
||||
def ping(host: str):
|
||||
ping_cmd = subprocess.Popen(['/usr/bin/ping', '-c1', '-W2', host], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
return ping_cmd
|
||||
|
||||
counter = 0
|
||||
ezrinet_last = False
|
||||
internet_last = False
|
||||
ezrinet_ping = None
|
||||
internet_ping = None
|
||||
|
||||
while True:
|
||||
if counter == 0:
|
||||
ezrinet_ping = ping('10.242.3.1')
|
||||
internet_ping = ping('1.1.1.1')
|
||||
if ezrinet_ping.poll() is not None:
|
||||
ezrinet_last = ezrinet_ping.returncode == 0
|
||||
if internet_ping.poll() is not None:
|
||||
internet_last = internet_ping.returncode == 0
|
||||
wifi_data = wifi()
|
||||
result = {
|
||||
"wifi": wifi_data,
|
||||
"network": {
|
||||
"wlan0": netdev("wlan0"),
|
||||
"ezrinet": netdev("ezrinet"),
|
||||
"wg-mullvad": netdev("mullvad"),
|
||||
},
|
||||
"connection": {
|
||||
"ezrinet": ezrinet_last,
|
||||
"internet": internet_last
|
||||
},
|
||||
"trusted": trusted(wifi_data.get("ssid", None))
|
||||
}
|
||||
print(json.dumps(result), flush=True)
|
||||
counter += 1
|
||||
# Send pings every 10 seconds
|
||||
counter %= 3
|
||||
sleep(1)
|
||||
@ -1,306 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import trparse
|
||||
from subprocess import run, PIPE, DEVNULL
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
from time import sleep, time as now
|
||||
from ipaddress import (
|
||||
IPv4Address as IPAddress,
|
||||
IPv4Network as IPNetwork,
|
||||
IPv4Interface as IPInterface,
|
||||
AddressValueError,
|
||||
)
|
||||
from netifaces import interfaces, ifaddresses, AF_INET, AF_LINK, gateways
|
||||
import dbus
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# These are networks that are considered secure. All traffic being routed
|
||||
# through one of these networks is being encrypted and routed through an
|
||||
# anonymizing service.
|
||||
SECURE_FIRSTHOP_NETWORKS = {
|
||||
"home": IPNetwork("10.242.0.0/16"), # Personal network
|
||||
"mullvad": IPNetwork("10.64.0.1/32"), # Mullvad VPN
|
||||
"usu": IPNetwork("129.123.8.126/32"), # USU VPN
|
||||
}
|
||||
|
||||
# Interfaces that we should check
|
||||
DEFAULT_INTERFACES_TO_WATCH = ["lan", "ezrinet"]
|
||||
# Interfaces that we should use when determining if we are online
|
||||
DEFAULT_INTERFACES_TO_COUNT = ["lan"]
|
||||
# Overrides for IP addresses to ping to determine if an interface is
|
||||
# online This is useful for interfaces that don't have or report a
|
||||
# gateway, but we still want to check if they're online, such as the
|
||||
# ezrinet interface.
|
||||
INTERFACE_PING_OVERRIDES = {"ezrinet": IPAddress("10.242.3.1")}
|
||||
|
||||
INTERFACES_TO_WATCH = os.environ.get(
|
||||
"NETWORK_INTERFACES_TO_WATCH", ",".join(DEFAULT_INTERFACES_TO_WATCH)
|
||||
).split(",")
|
||||
INTERFACES_TO_COUNT = os.environ.get(
|
||||
"NETWORK_INTERFACES_TO_COUNT", ",".join(DEFAULT_INTERFACES_TO_COUNT)
|
||||
).split(",")
|
||||
|
||||
for key, value in os.environ.items():
|
||||
if key[: len("NETWORK_PING_OVERRIDE_")] == "NETWORK_PING_OVERRIDE_":
|
||||
INTERFACE_PING_OVERRIDES[key[len("NETWORK_PING_OVERRIDE_") :]] = IPAddress(
|
||||
value
|
||||
)
|
||||
|
||||
|
||||
class Interface:
|
||||
name: str
|
||||
mac: str
|
||||
addresses: list[IPInterface]
|
||||
gateway: IPAddress | None
|
||||
|
||||
@property
|
||||
def online(self):
|
||||
# If we have no addresses, we're not online
|
||||
if len(self.addresses) == 0:
|
||||
return False
|
||||
# If we have an override, ping that
|
||||
if self.name in INTERFACE_PING_OVERRIDES:
|
||||
return ping(INTERFACE_PING_OVERRIDES[self.name])
|
||||
# If we have no gateway, this interface is likely not intended
|
||||
# to route beyond the local network, so we'll assume it's
|
||||
# online, at least for whatever it's used for.
|
||||
if self.gateway is None:
|
||||
return True
|
||||
return ping(self.gateway)
|
||||
|
||||
def asdict(self):
|
||||
return {
|
||||
"name": self.name,
|
||||
"mac": self.mac,
|
||||
"addresses": [
|
||||
{
|
||||
"address": str(addr.ip),
|
||||
"netmask": str(addr.netmask),
|
||||
"network": str(addr.network),
|
||||
"prefixlen": addr._prefixlen,
|
||||
}
|
||||
for addr in self.addresses
|
||||
],
|
||||
"online": self.online,
|
||||
}
|
||||
|
||||
|
||||
def get_first_hop() -> IPAddress | None:
|
||||
"""Get the first network hop."""
|
||||
# Use ping to get the first hop
|
||||
cmd = ["/usr/bin/ping", "-c1", "-W0.3", "-t1", "1.1.1.1"]
|
||||
result = run(cmd, stdout=PIPE, stderr=PIPE)
|
||||
try:
|
||||
ip = IPAddress(result.stdout.decode("utf-8").split("\n")[1].split()[1])
|
||||
except (IndexError, AddressValueError):
|
||||
# If we can't parse the output, return None
|
||||
return None
|
||||
return ip
|
||||
|
||||
|
||||
def validate_first_hop_is_secure(first_hop: IPAddress):
|
||||
"""Check if the first hop is in a secure network."""
|
||||
for name, network in SECURE_FIRSTHOP_NETWORKS.items():
|
||||
if first_hop in network:
|
||||
return name
|
||||
|
||||
|
||||
def get_gateways():
|
||||
"""Get gateways on each interface."""
|
||||
gw = gateways().get(AF_INET, [])
|
||||
result = {}
|
||||
for iface in INTERFACES_TO_WATCH:
|
||||
for gateway in gw:
|
||||
if gateway[1] == iface:
|
||||
result[iface] = gateway[0]
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def interface_status(interface: str, gw):
|
||||
"""Get the status of an interface."""
|
||||
try:
|
||||
addrs = ifaddresses(interface)
|
||||
except:
|
||||
addrs = {}
|
||||
result = Interface()
|
||||
result.name = interface
|
||||
result.gateway = gw.get(interface, None)
|
||||
if AF_LINK in addrs:
|
||||
result.mac = addrs[AF_LINK][0]["addr"]
|
||||
else:
|
||||
result.mac = None
|
||||
if AF_INET in addrs:
|
||||
result.addresses = [
|
||||
IPInterface(f'{addr["addr"]}/{addr["netmask"]}')
|
||||
for addr in addrs.get(AF_INET, [])
|
||||
]
|
||||
else:
|
||||
result.addresses = []
|
||||
return result
|
||||
|
||||
|
||||
def ping(host: IPAddress) -> bool:
|
||||
cmd = ["/usr/bin/ping", "-c1", "-w1", str(host)]
|
||||
result = run(cmd, stdout=DEVNULL, stderr=DEVNULL)
|
||||
return result.returncode == 0
|
||||
|
||||
|
||||
def get_public_ip():
|
||||
"""Get the public IP address."""
|
||||
cmd = ["/usr/bin/curl", "-s", "https://ipinfo.io"]
|
||||
result = run(cmd, stdout=PIPE, stderr=PIPE)
|
||||
try:
|
||||
data = json.loads(result.stdout.decode("utf-8"))
|
||||
except (IndexError, ValueError):
|
||||
return None
|
||||
try:
|
||||
# If the IP address is invalid, don't return anything
|
||||
IPAddress(data["ip"])
|
||||
except (AddressValueError, KeyError):
|
||||
if data.get("status", None) == 429:
|
||||
# We're rate limited, so return something indicating that
|
||||
return {"rate_limited": True}
|
||||
return None
|
||||
return data
|
||||
|
||||
|
||||
def get_default_route():
|
||||
"""Get the default route."""
|
||||
cmd = ["/usr/bin/ip", "route", "show", "default"]
|
||||
result = run(cmd, stdout=PIPE, stderr=PIPE)
|
||||
try:
|
||||
# Get first line (might have multiple gateway routes)
|
||||
line = result.stdout.decode("utf-8").split("\n")[0]
|
||||
# Get the gateway link (following "dev")
|
||||
link = re.search(r"dev\s+(\S+)", line).group(1)
|
||||
# We already know the gateway IP based on our firsthop check earlier, and that's more reliable since it will respect any routing rules
|
||||
except (IndexError, AttributeError):
|
||||
return None
|
||||
return link
|
||||
|
||||
|
||||
def format_time(time: datetime) -> dict[str, str | int]:
|
||||
"""Format a datetime object for display."""
|
||||
return {
|
||||
"hour": f"{time.hour:02}",
|
||||
"minute": f"{time.minute:02}",
|
||||
"second": f"{time.second:02}",
|
||||
"year": f"{time.year:04}",
|
||||
"month": f"{time.month:02}",
|
||||
"day": f"{time.day:02}",
|
||||
"unix": int(time.timestamp()),
|
||||
}
|
||||
|
||||
|
||||
# system_bus = dbus.SystemBus()
|
||||
# networkd = system_bus.get_object(
|
||||
# "org.freedesktop.network1", "/org/freedesktop/network1"
|
||||
# )
|
||||
# manager = dbus.Interface(networkd, "org.freedesktop.network1.Manager")
|
||||
|
||||
|
||||
# def get_dbus_interfaces():
|
||||
# for iface in INTERFACES_TO_WATCH:
|
||||
# dbus_object = system_bus.get_object(
|
||||
# "org.freedesktop.network1", manager.GetLinkByName(iface)
|
||||
# )
|
||||
# dbus_interface = dbus.Interface(dbus_object, "org.freedesktop.network1.Link")
|
||||
# yield dbus_interface
|
||||
|
||||
runtime_dir = os.environ.get("XDG_RUNTIME_DIR", "/tmp")
|
||||
|
||||
|
||||
def load_last_data(var_name: str):
|
||||
try:
|
||||
if os.path.exists(f"{runtime_dir}/{var_name}.json"):
|
||||
with open(f"{runtime_dir}/{var_name}.json", "r") as f:
|
||||
return json.load(f), os.path.getmtime(f"{runtime_dir}/{var_name}.json")
|
||||
|
||||
except:
|
||||
pass
|
||||
return None, None
|
||||
|
||||
|
||||
def store_last_data(var_name: str, data):
|
||||
with open(f"{runtime_dir}/{var_name}.json", "w") as f:
|
||||
json.dump(data, f)
|
||||
|
||||
|
||||
last_default_route = load_last_data("default_route")[0]
|
||||
last_ip_data, last_request = load_last_data("ip_data")
|
||||
try:
|
||||
last_first_hop = IPAddress(load_last_data("first_hop")[0])
|
||||
except:
|
||||
last_first_hop = None
|
||||
|
||||
while True:
|
||||
try:
|
||||
online = ping("1.1.1.1")
|
||||
except:
|
||||
online = False
|
||||
hop = get_first_hop()
|
||||
gw = get_gateways()
|
||||
default_route = get_default_route()
|
||||
# public IP shouldn't change often, so only check every 2 hours or
|
||||
# if the default route or first hop changes
|
||||
if (
|
||||
default_route != last_default_route
|
||||
or last_ip_data is None
|
||||
or last_request is None
|
||||
or now() - last_request > (2 * 60 * 60)
|
||||
or (hop != last_first_hop and hop is not None)
|
||||
):
|
||||
print("refreshing public IP", file=sys.stderr, flush=True)
|
||||
public_ip_data = get_public_ip()
|
||||
if public_ip_data is None:
|
||||
print("failed to get public IP", file=sys.stderr, flush=True)
|
||||
elif public_ip_data.get("rate_limited", False):
|
||||
last_ip_data = public_ip_data
|
||||
public_ip_data = None
|
||||
else:
|
||||
last_ip_data = public_ip_data
|
||||
# Write the public IP data to a file
|
||||
store_last_data("ip_data", public_ip_data)
|
||||
last_default_route = default_route
|
||||
store_last_data("default_route", default_route)
|
||||
last_request = now()
|
||||
last_first_hop = hop
|
||||
store_last_data("first_hop", str(hop))
|
||||
else:
|
||||
public_ip_data = last_ip_data
|
||||
if hop is not None:
|
||||
secure_msg = validate_first_hop_is_secure(hop)
|
||||
secure = secure_msg is not None
|
||||
else:
|
||||
# If we can't reach the router, assume insecure
|
||||
secure = False
|
||||
iface_data = [interface_status(iface, gw) for iface in INTERFACES_TO_WATCH]
|
||||
iface_dict = {iface.name: iface.asdict() for iface in iface_data}
|
||||
|
||||
default_route_iface_data = interface_status(default_route, gw)
|
||||
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"online": online,
|
||||
"secure": secure,
|
||||
"interfaces": iface_dict,
|
||||
"public_ip": public_ip_data or {},
|
||||
"have_public_ip": public_ip_data is not None and "ip" in public_ip_data,
|
||||
"default_route": default_route,
|
||||
"default_interface": default_route_iface_data.asdict(),
|
||||
"have_default_route": default_route is not None,
|
||||
"gateway": str(hop),
|
||||
"have_gateway": hop is not None,
|
||||
"last_update": format_time(datetime.now()),
|
||||
}
|
||||
),
|
||||
flush=True,
|
||||
)
|
||||
|
||||
sleep(5)
|
||||
@ -2,9 +2,89 @@
|
||||
;; Top-Level Module Definitions ;;
|
||||
;;;; ;;;;
|
||||
|
||||
;;; ;;;
|
||||
;; PolKit Auth Window ;;
|
||||
;;; ;;;
|
||||
;;; ;;;
|
||||
;; Generic Windows ;;
|
||||
;;; ;;;
|
||||
|
||||
(defwidget sidebar [orientation]
|
||||
(box :orientation "v"
|
||||
:valign "start"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:class "root ${orientation}-side"
|
||||
(sideclock)
|
||||
(network-sidebar-details)
|
||||
(system-gauges)
|
||||
(volume-gauges)))
|
||||
|
||||
(defwidget ws-group-rightalign [workspace-group]
|
||||
(box :orientation "h"
|
||||
:halign "end"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:class "rightbox"
|
||||
(sway-workspace :group {workspace-group})
|
||||
(sway-workspaces :group {workspace-group})))
|
||||
|
||||
(defwidget ws-group-leftalign [workspace-group]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:class "leftbox"
|
||||
(sway-workspaces :group {workspace-group})
|
||||
(sway-workspace :group {workspace-group})))
|
||||
|
||||
|
||||
(defwidget desktop-details-rightalign []
|
||||
(box :orientation "h"
|
||||
:halign "end"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:class "rightbox"
|
||||
(vpn-network)
|
||||
(mpris2)
|
||||
(network)
|
||||
(system-memory)
|
||||
(system-cpu-avg)
|
||||
(clock)))
|
||||
|
||||
(defwidget hostname-centeralign []
|
||||
(box :orientation "h"
|
||||
:halign "center"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:class "centerbox"
|
||||
(system-name)))
|
||||
|
||||
(defwidget hostname-leftalign []
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:class "leftbox"
|
||||
(system-name)))
|
||||
|
||||
(defwidget hostname-rightalign []
|
||||
(box :orientation "h"
|
||||
:halign "end"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:class "rightbox"
|
||||
(system-name)))
|
||||
|
||||
(defwidget laptop-details-rightalign [?battery]
|
||||
(box :orientation "h"
|
||||
:halign "end"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:class "rightbox"
|
||||
(vpn-network)
|
||||
(network)
|
||||
(system-battery :battery {battery ?: "BAT1"})
|
||||
(audio)
|
||||
(clock)))
|
||||
|
||||
|
||||
;;; ;;;
|
||||
;; Bars for Rocinante ;;
|
||||
|
||||
@ -167,7 +167,11 @@
|
||||
"names": ["eDP-1"],
|
||||
"group": "builtin",
|
||||
"position": [0, 600],
|
||||
"eww_windows": ["builtinbar"],
|
||||
"eww_windows": {
|
||||
"laptopbar": {
|
||||
"battery": "BAT1"
|
||||
}
|
||||
},
|
||||
"mode": "2560x1600@165Hz scale 1.5 color_profile icc /usr/share/color/icc/colord/BOE_CQ_______NE160QDM_NZ6.icm"
|
||||
},
|
||||
{
|
||||
@ -177,7 +181,12 @@
|
||||
"group": "left",
|
||||
"position": [1707, 0],
|
||||
"mode": "2560x1440@120Hz",
|
||||
"eww_windows": ["leftbar", "sidebar"]
|
||||
"eww_windows": {
|
||||
"desktop-leftbar": {},
|
||||
"sidebar": {
|
||||
"side": "left"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"make": "Dell Inc.",
|
||||
@ -186,7 +195,7 @@
|
||||
"group": "center",
|
||||
"position": [4267, 0],
|
||||
"mode": "3840x1600",
|
||||
"eww_windows": ["centerbar"]
|
||||
"eww_windows": ["desktop-mainbar"]
|
||||
},
|
||||
{
|
||||
"make": "Acer Technologies",
|
||||
@ -195,7 +204,12 @@
|
||||
"group": "right",
|
||||
"position": [8107, 0],
|
||||
"mode": "2560x1440@120Hz",
|
||||
"eww_windows": ["rightbar"]
|
||||
"eww_windows": {
|
||||
"desktop-rightbar": {},
|
||||
"sidebar": {
|
||||
"side": "right"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"groups": {
|
||||
@ -224,7 +238,11 @@
|
||||
"names": ["eDP-1"],
|
||||
"group": "builtin",
|
||||
"position": [0, 600],
|
||||
"eww_windows": ["builtinbar"],
|
||||
"eww_windows": {
|
||||
"laptopbar": {
|
||||
"battery": "BAT1"
|
||||
}
|
||||
},
|
||||
"mode": "2560x1600@165Hz scale 1.5 color_profile icc /usr/share/color/icc/colord/BOE_CQ_______NE160QDM_NZ6.icm"
|
||||
},
|
||||
{
|
||||
@ -234,7 +252,12 @@
|
||||
"group": "left",
|
||||
"position": [1707, 200],
|
||||
"mode": "1920x1080@75Hz",
|
||||
"eww_windows": ["leftbar", "sidebar"]
|
||||
"eww_windows": {
|
||||
"desktop-leftbar": {},
|
||||
"sidebar": {
|
||||
"side": "left"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"make": "ASUSTek COMPUTER INC",
|
||||
@ -243,7 +266,7 @@
|
||||
"group": "center",
|
||||
"position": [3627, 0],
|
||||
"mode": "2560x1440@165Hz",
|
||||
"eww_windows": ["centerbar"]
|
||||
"eww_windows": ["desktop-mainbar"]
|
||||
},
|
||||
{
|
||||
"make": "ASUSTek COMPUTER INC",
|
||||
@ -252,7 +275,7 @@
|
||||
"group": "right",
|
||||
"position": [6187, 200],
|
||||
"mode": "1920x1080@75Hz",
|
||||
"eww_windows": ["rightbar"]
|
||||
"eww_windows": ["desktop-rightbar"]
|
||||
}
|
||||
],
|
||||
"groups": {
|
||||
@ -281,7 +304,11 @@
|
||||
"names": ["eDP-1"],
|
||||
"group": "builtin",
|
||||
"position": [0, 0],
|
||||
"eww_windows": ["builtinbar"],
|
||||
"eww_windows": {
|
||||
"laptopbar": {
|
||||
"battery": "BAT1"
|
||||
}
|
||||
},
|
||||
"mode": "2560x1600@165Hz scale 1.25 color_profile icc /usr/share/color/icc/colord/BOE_CQ_______NE160QDM_NZ6.icm"
|
||||
}
|
||||
],
|
||||
@ -299,7 +326,11 @@
|
||||
"names": ["eDP-1"],
|
||||
"group": "builtin",
|
||||
"position": [0, 0],
|
||||
"eww_windows": ["builtinbar"],
|
||||
"eww_windows": {
|
||||
"laptopbar": {
|
||||
"battery": "BAT1"
|
||||
}
|
||||
},
|
||||
"mode": "2560x1600@165Hz scale 1.25 color_profile icc /usr/share/color/icc/colord/BOE_CQ_______NE160QDM_NZ6.icm"
|
||||
}
|
||||
],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user