commit
7c3f545438
@ -30,6 +30,7 @@ MOCK_MODULES = [
|
|||||||
"i3pystatus.pulseaudio.pulse",
|
"i3pystatus.pulseaudio.pulse",
|
||||||
"notmuch",
|
"notmuch",
|
||||||
"requests",
|
"requests",
|
||||||
|
"bs4"
|
||||||
]
|
]
|
||||||
|
|
||||||
for mod_name in MOCK_MODULES:
|
for mod_name in MOCK_MODULES:
|
||||||
|
@ -439,7 +439,16 @@ def user_open(url_or_command):
|
|||||||
scheme = urlparse(url_or_command).scheme
|
scheme = urlparse(url_or_command).scheme
|
||||||
if scheme == 'http' or scheme == 'https':
|
if scheme == 'http' or scheme == 'https':
|
||||||
import webbrowser
|
import webbrowser
|
||||||
webbrowser.open(url_or_command)
|
import os
|
||||||
|
# webbrowser.open() sometimes prints a message for some reason and confuses i3
|
||||||
|
# Redirect stdout briefly to prevent this from happening.
|
||||||
|
savout = os.dup(1)
|
||||||
|
os.close(1)
|
||||||
|
os.open(os.devnull, os.O_RDWR)
|
||||||
|
try:
|
||||||
|
webbrowser.open(url_or_command)
|
||||||
|
finally:
|
||||||
|
os.dup2(savout, 1)
|
||||||
else:
|
else:
|
||||||
import subprocess
|
import subprocess
|
||||||
subprocess.Popen(url_or_command, shell=True)
|
subprocess.Popen(url_or_command, shell=True)
|
||||||
|
59
i3pystatus/github.py
Normal file
59
i3pystatus/github.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
from i3pystatus import IntervalModule
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
from i3pystatus.core import ConfigError
|
||||||
|
from i3pystatus.core.util import user_open, internet, require
|
||||||
|
|
||||||
|
|
||||||
|
class Github(IntervalModule):
|
||||||
|
"""
|
||||||
|
Check Github for pending notifications.
|
||||||
|
Requires `requests`
|
||||||
|
|
||||||
|
Formatters:
|
||||||
|
|
||||||
|
* `{unread}` - contains the value of unread_marker when there are pending notifications
|
||||||
|
* `{unread_count}` - number of unread notifications, empty if 0
|
||||||
|
"""
|
||||||
|
|
||||||
|
unread_marker = u"●"
|
||||||
|
unread = ''
|
||||||
|
color = '#78EAF2'
|
||||||
|
username = ''
|
||||||
|
password = ''
|
||||||
|
format = '{unread}'
|
||||||
|
interval = 600
|
||||||
|
|
||||||
|
on_leftclick = 'open_github'
|
||||||
|
|
||||||
|
settings = (
|
||||||
|
('format', 'format string'),
|
||||||
|
('unread_marker', 'sets the string that the "unread" formatter shows when there are pending notifications'),
|
||||||
|
("username", ""),
|
||||||
|
("password", ""),
|
||||||
|
("color", "")
|
||||||
|
)
|
||||||
|
|
||||||
|
def open_github(self):
|
||||||
|
user_open('https://github.com/' + self.username)
|
||||||
|
|
||||||
|
@require(internet)
|
||||||
|
def run(self):
|
||||||
|
format_values = dict(unread_count='', unread='')
|
||||||
|
|
||||||
|
response = requests.get('https://api.github.com/notifications', auth=(self.username, self.password))
|
||||||
|
data = json.loads(response.text)
|
||||||
|
|
||||||
|
# Bad credentials
|
||||||
|
if isinstance(data, dict):
|
||||||
|
raise ConfigError(data['message'])
|
||||||
|
|
||||||
|
unread = len(data)
|
||||||
|
if unread > 0:
|
||||||
|
format_values['unread_count'] = unread
|
||||||
|
format_values['unread'] = self.unread_marker
|
||||||
|
|
||||||
|
self.output = {
|
||||||
|
'full_text': self.format.format(**format_values),
|
||||||
|
'color': self.color
|
||||||
|
}
|
@ -1,7 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import netifaces
|
import netifaces
|
||||||
import basiciw
|
|
||||||
import psutil
|
|
||||||
from i3pystatus import IntervalModule
|
from i3pystatus import IntervalModule
|
||||||
from i3pystatus.core.color import ColorRangeModule
|
from i3pystatus.core.color import ColorRangeModule
|
||||||
from i3pystatus.core.util import make_graph, round_dict, make_bar
|
from i3pystatus.core.util import make_graph, round_dict, make_bar
|
||||||
@ -74,7 +72,7 @@ class NetworkInfo():
|
|||||||
Retrieve network information.
|
Retrieve network information.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, interface, ignore_interfaces, detached_down, unknown_up):
|
def __init__(self, interface, ignore_interfaces, detached_down, unknown_up, get_wifi_info=False):
|
||||||
if interface not in netifaces.interfaces() and not detached_down:
|
if interface not in netifaces.interfaces() and not detached_down:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Unknown interface {iface}!".format(iface=interface))
|
"Unknown interface {iface}!".format(iface=interface))
|
||||||
@ -82,6 +80,7 @@ class NetworkInfo():
|
|||||||
self.ignore_interfaces = ignore_interfaces
|
self.ignore_interfaces = ignore_interfaces
|
||||||
self.detached_down = detached_down
|
self.detached_down = detached_down
|
||||||
self.unknown_up = unknown_up
|
self.unknown_up = unknown_up
|
||||||
|
self.get_wifi_info = get_wifi_info
|
||||||
|
|
||||||
def get_info(self, interface):
|
def get_info(self, interface):
|
||||||
format_dict = dict(v4="", v4mask="", v4cidr="", v6="", v6mask="", v6cidr="")
|
format_dict = dict(v4="", v4mask="", v4cidr="", v6="", v6mask="", v6cidr="")
|
||||||
@ -133,9 +132,15 @@ class NetworkInfo():
|
|||||||
break
|
break
|
||||||
return info
|
return info
|
||||||
|
|
||||||
@staticmethod
|
def extract_wireless_info(self, interface):
|
||||||
def extract_wireless_info(interface):
|
|
||||||
info = dict(essid="", freq="", quality=0.0, quality_bar="")
|
info = dict(essid="", freq="", quality=0.0, quality_bar="")
|
||||||
|
|
||||||
|
# Just return empty values if we're not using any Wifi functionality
|
||||||
|
if not self.get_wifi_info:
|
||||||
|
return info
|
||||||
|
|
||||||
|
import basiciw
|
||||||
|
|
||||||
try:
|
try:
|
||||||
iwi = basiciw.iwinfo(interface)
|
iwi = basiciw.iwinfo(interface)
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -169,6 +174,8 @@ class NetworkTraffic():
|
|||||||
self.round_size = round_size
|
self.round_size = round_size
|
||||||
|
|
||||||
def update_counters(self, interface):
|
def update_counters(self, interface):
|
||||||
|
import psutil
|
||||||
|
|
||||||
self.pnic_before = self.pnic
|
self.pnic_before = self.pnic
|
||||||
counters = psutil.net_io_counters(pernic=True)
|
counters = psutil.net_io_counters(pernic=True)
|
||||||
self.pnic = counters[interface] if interface in counters else None
|
self.pnic = counters[interface] if interface in counters else None
|
||||||
@ -282,24 +289,41 @@ class Network(IntervalModule, ColorRangeModule):
|
|||||||
|
|
||||||
on_leftclick = "nm-connection-editor"
|
on_leftclick = "nm-connection-editor"
|
||||||
on_rightclick = "cycle_interface"
|
on_rightclick = "cycle_interface"
|
||||||
|
on_upscroll = ['cycle_interface', 1]
|
||||||
|
on_downscroll = ['cycle_interface', -1]
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
self.network_traffic = NetworkTraffic(self.unknown_up, self.divisor, self.round_size)
|
# Don't require importing basiciw unless using the functionality it offers.
|
||||||
self.network_info = NetworkInfo(self.interface, self.ignore_interfaces, self.detached_down, self.unknown_up)
|
if any(s in self.format_up or s in self.format_up for s in
|
||||||
|
['essid', 'freq', 'quality', 'quality_bar']):
|
||||||
|
get_wifi_info = True
|
||||||
|
else:
|
||||||
|
get_wifi_info = False
|
||||||
|
|
||||||
|
self.network_info = NetworkInfo(self.interface, self.ignore_interfaces, self.detached_down, self.unknown_up,
|
||||||
|
get_wifi_info)
|
||||||
|
|
||||||
|
# Don't require importing psutil unless using the functionality it offers.
|
||||||
|
if any(s in self.format_up or s in self.format_down for s in
|
||||||
|
['bytes_sent', 'bytes_recv', 'packets_sent', 'packets_recv', 'network_graph']):
|
||||||
|
self.network_traffic = NetworkTraffic(self.unknown_up, self.divisor, self.round_size)
|
||||||
|
else:
|
||||||
|
self.network_traffic = None
|
||||||
|
|
||||||
self.colors = self.get_hex_color_range(self.start_color, self.end_color, int(self.upper_limit))
|
self.colors = self.get_hex_color_range(self.start_color, self.end_color, int(self.upper_limit))
|
||||||
self.kbs_arr = [0.0] * self.graph_width
|
self.kbs_arr = [0.0] * self.graph_width
|
||||||
|
|
||||||
def cycle_interface(self):
|
def cycle_interface(self, increment=1):
|
||||||
interfaces = [i for i in netifaces.interfaces() if i not in self.ignore_interfaces]
|
interfaces = [i for i in netifaces.interfaces() if i not in self.ignore_interfaces]
|
||||||
if self.interface in interfaces:
|
if self.interface in interfaces:
|
||||||
next_index = (interfaces.index(self.interface) + 1) % len(interfaces)
|
next_index = (interfaces.index(self.interface) + increment) % len(interfaces)
|
||||||
self.interface = interfaces[next_index]
|
self.interface = interfaces[next_index]
|
||||||
elif len(interfaces) > 0:
|
elif len(interfaces) > 0:
|
||||||
self.interface = interfaces[0]
|
self.interface = interfaces[0]
|
||||||
|
|
||||||
self.network_traffic.clear_counters()
|
if self.network_traffic:
|
||||||
self.kbs_arr = [0.0] * self.graph_width
|
self.network_traffic.clear_counters()
|
||||||
|
self.kbs_arr = [0.0] * self.graph_width
|
||||||
|
|
||||||
def get_network_graph(self, kbs):
|
def get_network_graph(self, kbs):
|
||||||
# Cycle array by inserting at the start and chopping off the last element
|
# Cycle array by inserting at the start and chopping off the last element
|
||||||
@ -311,28 +335,27 @@ class Network(IntervalModule, ColorRangeModule):
|
|||||||
format_values = dict(kbs="", network_graph="", bytes_sent="", bytes_recv="", packets_sent="", packets_recv="",
|
format_values = dict(kbs="", network_graph="", bytes_sent="", bytes_recv="", packets_sent="", packets_recv="",
|
||||||
interface="", v4="", v4mask="", v4cidr="", v6="", v6mask="", v6cidr="", mac="",
|
interface="", v4="", v4mask="", v4cidr="", v6="", v6mask="", v6cidr="", mac="",
|
||||||
essid="", freq="", quality="", quality_bar="")
|
essid="", freq="", quality="", quality_bar="")
|
||||||
|
color = None
|
||||||
|
if self.network_traffic:
|
||||||
|
network_usage = self.network_traffic.get_usage(self.interface)
|
||||||
|
format_values.update(network_usage)
|
||||||
|
if self.graph_type == 'input':
|
||||||
|
kbs = network_usage['bytes_recv']
|
||||||
|
elif self.graph_type == 'output':
|
||||||
|
kbs = network_usage['bytes_sent']
|
||||||
|
else:
|
||||||
|
raise Exception("graph_type must be either 'input' or 'output'!")
|
||||||
|
|
||||||
network_usage = self.network_traffic.get_usage(self.interface)
|
format_values['network_graph'] = self.get_network_graph(kbs)
|
||||||
format_values.update(network_usage)
|
format_values['kbs'] = "{0:.1f}".format(round(kbs, 2)).rjust(6)
|
||||||
|
color = self.get_gradient(kbs, self.colors, self.upper_limit)
|
||||||
|
|
||||||
network_info = self.network_info.get_info(self.interface)
|
network_info = self.network_info.get_info(self.interface)
|
||||||
format_values.update(network_info)
|
format_values.update(network_info)
|
||||||
|
|
||||||
if self.graph_type == 'input':
|
|
||||||
kbs = network_usage['bytes_recv']
|
|
||||||
elif self.graph_type == 'output':
|
|
||||||
kbs = network_usage['bytes_sent']
|
|
||||||
else:
|
|
||||||
raise Exception("graph_type must be either 'input' or 'output'!")
|
|
||||||
|
|
||||||
format_values['network_graph'] = self.get_network_graph(kbs)
|
|
||||||
format_values['kbs'] = "{0:.1f}".format(round(kbs, 2)).rjust(6)
|
|
||||||
format_values['interface'] = self.interface
|
format_values['interface'] = self.interface
|
||||||
|
|
||||||
if sysfs_interface_up(self.interface, self.unknown_up):
|
if sysfs_interface_up(self.interface, self.unknown_up):
|
||||||
if self.dynamic_color:
|
if not self.dynamic_color:
|
||||||
color = self.get_gradient(kbs, self.colors, self.upper_limit)
|
|
||||||
else:
|
|
||||||
color = self.color_up
|
color = self.color_up
|
||||||
|
|
||||||
self.output = {
|
self.output = {
|
||||||
|
@ -62,7 +62,6 @@ class Reddit(IntervalModule):
|
|||||||
}
|
}
|
||||||
|
|
||||||
on_leftclick = "open_permalink"
|
on_leftclick = "open_permalink"
|
||||||
on_click = "open_link"
|
|
||||||
|
|
||||||
_permalink = ""
|
_permalink = ""
|
||||||
_url = ""
|
_url = ""
|
||||||
@ -136,6 +135,9 @@ class Reddit(IntervalModule):
|
|||||||
"color": color,
|
"color": color,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def open_mail(self):
|
||||||
|
user_open('https://www.reddit.com/message/unread/')
|
||||||
|
|
||||||
def open_permalink(self):
|
def open_permalink(self):
|
||||||
user_open(self._permalink)
|
user_open(self._permalink)
|
||||||
|
|
||||||
|
107
i3pystatus/whosonlocation.py
Normal file
107
i3pystatus/whosonlocation.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
from i3pystatus import IntervalModule
|
||||||
|
import requests
|
||||||
|
from collections import OrderedDict
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
|
||||||
|
class WhosOnLocation():
|
||||||
|
|
||||||
|
email = None
|
||||||
|
password = None
|
||||||
|
session = None
|
||||||
|
|
||||||
|
def __init__(self, email, password):
|
||||||
|
self.email = email
|
||||||
|
self.password = password
|
||||||
|
self.session = requests.Session()
|
||||||
|
|
||||||
|
def login(self):
|
||||||
|
login_details = {'email_input': self.email,
|
||||||
|
'password_input': self.password,
|
||||||
|
'_redirect_url': '',
|
||||||
|
'continue_submit': 'Login'}
|
||||||
|
r = self.session.post('https://login.whosonlocation.com/login', data=login_details)
|
||||||
|
return r.url == 'https://au.whosonlocation.com/home?justloggedin=true'
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
r = self.session.get('https://au.whosonlocation.com/home?justloggedin=true')
|
||||||
|
html = BeautifulSoup(r.content)
|
||||||
|
status = html.body.find("span", {"class": "my-status-name"})
|
||||||
|
if status:
|
||||||
|
return status.text
|
||||||
|
|
||||||
|
def on_site(self):
|
||||||
|
return self.__change_status('onsite')
|
||||||
|
|
||||||
|
def off_site(self):
|
||||||
|
return self.__change_status('offsite')
|
||||||
|
|
||||||
|
def __change_status(self, status):
|
||||||
|
r = self.session.post('https://au.whosonlocation.com/ajax/changestatus', data={'status': status})
|
||||||
|
return r.json()
|
||||||
|
|
||||||
|
# _type can be org or location
|
||||||
|
def search(self, keyword, _type='location'):
|
||||||
|
payload = {'keyword': keyword, 'type': _type}
|
||||||
|
r = self.session.get('https://au.whosonlocation.com/home/search', params=payload)
|
||||||
|
return self.__parse_results(BeautifulSoup(r.content))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __parse_results(page):
|
||||||
|
titles = ['Name', 'Title', 'Department', 'Current Location', 'Home Location']
|
||||||
|
table = page.body.find_all("tr", {"class": "dataRow"})
|
||||||
|
results = []
|
||||||
|
for row in table:
|
||||||
|
values = [v.string for v in row.findAll('td', {'class': 'truncate'})]
|
||||||
|
results.append(OrderedDict(zip(titles, values)))
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
class WOL(IntervalModule):
|
||||||
|
"""
|
||||||
|
Change your whosonlocation.com status.
|
||||||
|
|
||||||
|
Requires the PyPi module `beautifulsoup4`
|
||||||
|
"""
|
||||||
|
location = None
|
||||||
|
email = None
|
||||||
|
password = None
|
||||||
|
|
||||||
|
settings = (
|
||||||
|
'email',
|
||||||
|
'password'
|
||||||
|
)
|
||||||
|
|
||||||
|
color_on_site = '#00FF00'
|
||||||
|
color_off_site = '#ff0000'
|
||||||
|
format = 'Status: {status}'
|
||||||
|
status = None
|
||||||
|
|
||||||
|
on_leftclick = 'change_status'
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.location = WhosOnLocation(self.email, self.password)
|
||||||
|
if not self.location.login():
|
||||||
|
raise Exception("Failed to login")
|
||||||
|
|
||||||
|
def change_status(self):
|
||||||
|
if self.status == 'On-Site':
|
||||||
|
self.location.off_site()
|
||||||
|
elif self.status == 'Off-Site':
|
||||||
|
self.location.on_site()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.status = self.location.get_status()
|
||||||
|
color = None
|
||||||
|
|
||||||
|
if self.status == 'Off-Site':
|
||||||
|
color = self.color_off_site
|
||||||
|
elif self.status == 'On-Site':
|
||||||
|
color = self.color_on_site
|
||||||
|
|
||||||
|
self.output = {
|
||||||
|
"full_text": self.format.format(
|
||||||
|
status=self.status
|
||||||
|
),
|
||||||
|
"color": color
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user