commit 1ff669a2a614451b36e912723dff74ae64f68cef Author: Stefano Mazzucco Date: Tue Oct 4 22:56:52 2016 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d6cb41 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*~ +luacov.* +*.rock diff --git a/README.md b/README.md new file mode 100644 index 0000000..047b5c9 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# A widget for the Awesome Window Manager to monitor the network with Connman + +This widget uses the +[`connman_dbus`](https://luarocks.org/modules/stefano-m/connman_dbus) +library. + +# Requirements + +In addition to the requirements listed in the `rockspec` file, you will need +the [Awesome Window Manager](https://awesomewm.org) +and Connman (for more information about this, see the +[`connman_dbus`](https://luarocks.org/modules/stefano-m/connman_dbus) +documentation). + +You will also need the DBus headers (`dbus.h`) installed. +For example, Debian and Ubuntu provide the DBus headers with the `libdbus-1-dev` +package, Fedora, RedHad and CentOS provide them with the `dbus-devel` package, +while Arch provides them (alongside the binaries) with the `libdbus` package. + +# Installation + +## Using Luarocks + +Probably, the easiest way to install this widget is to use `luarocks`: + + luarocks install connman_widget + +You can use the `--local` option if you don't want or can't install +it system-wide + +This will ensure that all its dependencies are installed. + +### A note about ldbus + +This module depends on the [`ldbus`](https://github.com/daurnimator/ldbus) +module that provides the low-level DBus bindings + + luarocks install --server=http://luarocks.org/manifests/daurnimator \ + ldbus \ + DBUS_INCDIR=/usr/include/dbus-1.0/ \ + DBUS_ARCH_INCDIR=/usr/lib/dbus-1.0/include + +As usual, you can use the `--local` option if you don't want or can't install +it system-wide. + +## From source + +Alternatively, you can copy the `connman_widget.lua` file in your +`~/.config/awesome` folder. You will have to install all the dependencies +manually though (see the `rockspec` file for more information). + +# Configuration + +The widget displays network icons that are searched in the folder defined +by `beautiful.connman_icon_theme_dir` with extension +`beautiful.connman_icon_extension`. +The default is to look into `"/usr/share/icons/Adwaita/scalable"` for +icons whose extension is `".svg"`. + +Depending on your network devices, you may need some or all of the icons +whose name starts with `network-`. + +You can specify a GUI client to be launched when the widget is right-clicked. +This can be done by changing the `gui_client` field of the widget. The default +is [econnman-bin](https://git.enlightenment.org/apps/econnman.git/) that needs +the [EFL libraries and their Python bindings](https://www.enlightenment.org/); +other [Desktop clients are also available](https://wiki.archlinux.org/index.php/Connman#Desktop_clients). + +# Mouse controls + +When the widget is focused: + +* Right button: launches GUI client (defined by the `gui_client` field; defaults to `econnman-bin`) + +# Tooltip + +A tooltip with the currently connected network is shown. It will simply +say `Wired` for a wired connection, or it will show the WiFi SSID and signal +strenght for a wireless connection. + +# Usage + +Add the following to your `~/.config/awesome/rc.lua`: + +Require the module: + + -- require *after* `beautiful.init` or the theme will be inconsistent! + local connman = require("connman_widget") + +Add the widget to your layout: + + right_layout:add(connman) + +# Limitations + +Currently, only wired (ethernet) and WiFi connections are supported. diff --git a/connman_widget-devel-1.rockspec b/connman_widget-devel-1.rockspec new file mode 100644 index 0000000..1dda1fc --- /dev/null +++ b/connman_widget-devel-1.rockspec @@ -0,0 +1,23 @@ +package = "connman_widget" + version = "devel-1" + source = { + url = "git://github.com/stefano-m/awesome-connman_widget", + tag = "master" + } + description = { + summary = "A Connman widget for the Awesome Window Manager", + detailed = [[ + Monitor your network devices in Awesome with Connman and DBus. + ]], + homepage = "https://github.com/stefano-m/awesome-connman_widget", + license = "GPL v3" + } + dependencies = { + "lua >= 5.1", + "connman_dbus", + } + supported_platforms = { "linux" } + build = { + type = "builtin", + modules = { connman_widget = "connman_widget.lua" }, + } diff --git a/connman_widget.lua b/connman_widget.lua new file mode 100644 index 0000000..7abc867 --- /dev/null +++ b/connman_widget.lua @@ -0,0 +1,249 @@ +-- Connman network widget +local awful = require("awful") +local beautiful = require("beautiful") +local naughty = require("naughty") +local wibox = require("wibox") + +-- Awesome DBus C API +local cdbus = dbus -- luacheck: ignore + +local Manager = require("connman_dbus") +local spawn_with_shell = awful.util.spawn_with_shell or awful.spawn.with_shell + +local icon_theme_dir = "/usr/share/icons/Adwaita/scalable/" +local icon_theme_extension = ".svg" +icon_theme_dir = beautiful.connman_icon_theme_dir or icon_theme_dir +icon_theme_extension = beautiful.connman_icon_theme_extension or icon_theme_extension + +local function default_table(t, default_value) + t = t or {} + local mt = { + __index = function (tbl, key) + if type(default_value) == "table" then + -- create a new table for each new key + default_value = {} + end + rawset(tbl, key, default_value) + return default_value + end + } + setmetatable(t, mt) + return t +end + +local icon_statuses = default_table( + { + cellular = { + three_g = "status/network-cellular-3g-symbolic", + four_g = "status/network-cellular-4g-symbolic", + acquiring = "status/network-cellular-acquiring-symbolic", + connected = "status/network-cellular-connected-symbolic", + edge = "status/network-cellular-edge-symbolic", + gprs = "status/network-cellular-gprs-symbolic", + hspa = "status/network-cellular-hspa-symbolic", + no_route = "status/network-cellular-no-route-symbolic", + offline = "status/network-cellular-offline-symbolic", + signal = { + excellent = "status/network-cellular-signal-excellent-symbolic", + good = "status/network-cellular-signal-good-symbolic", + none = "status/network-cellular-signal-none-symbolic", + ok = "status/network-cellular-signal-ok-symbolic", + weak = "status/network-cellular-signal-weak-symbolic", + } + }, + unspecified = { + err = "status/network-error-symbolic", + idle = "status/network-idle-symbolic", + no_route = "status/network-no-route-symbolic", + offline = "status/network-offline-symbolic", + receive = "status/network-receive-symbolic", + transmis_receive = "status/network-transmit-receive-symbolic", + transmit = "status/network-transmit-symbolic", + }, + vpn = { + acquiring = "status/network-vpn-acquiring-symbolic", + connected = "status/network-vpn-symbolic", + }, + ethernet = { + acquiring = "network-wired-acquiring-symbolic", + disconnected = "status/network-wired-disconnected-symbolic", + no_route = "status/network-wired-no-route-symbolic", + offline = "status/network-wired-offline-symbolic", + connected = "devices/network-wired-symbolic", -- this is different! + }, + wifi = { + acquiring = "status/network-wireless-acquiring-symbolic", + connected = "status/network-wireless-connected-symbolic", + encrypted = "status/network-wireless-encrypted-symbolic", + hotspot = "status/network-wireless-hotspot-symbolic", + no_route = "status/network-wireless-no-route-symbolic", + offline = "status/network-wireless-offline-symbolic", + signal = { + excellent = "status/network-wireless-signal-excellent-symbolic", + good = "status/network-wireless-signal-good-symbolic", + ok = "status/network-wireless-signal-ok-symbolic", + weak = "status/network-wireless-signal-weak-symbolic", + none = "status/network-wireless-signal-none-symbolic", + }, + }, + }, + {}) + +local show_signal = {ready = true, online = true} + +local function build_icon_path(name) + return icon_theme_dir .. name .. icon_theme_extension +end + +local function get_wifi_icon(service) + local states = { + idle = "no_route", -- is this correct? + failure = "offline", + association = "acquiring", + configuration = "acquiring", + disconnect = "offline", + } + if show_signal[service.State] then + local s = service.Strength + local v + if s <= 0 then + v = "none" + elseif s <= 25 then + v = "weak" + elseif s <= 50 then + v = "ok" + elseif s <= 75 then + v = "good" + else + v = "excellent" + end + return build_icon_path(icon_statuses.wifi.signal[v]) + else + return build_icon_path(icon_statuses.wifi[states[service.State]]) + end +end + +local function get_wired_icon(service) + local states = { + idle = "no_route", -- is this correct? + failure = "offline", + association = "acquiring", + configuration = "acquiring", + ready = "connected", + disconnect = "disconnected", + online = "connected", + } + return build_icon_path(icon_statuses.ethernet[states[service.State]]) +end + +local service_types = { + ethernet = get_wired_icon, + wifi = get_wifi_icon +} + +local function update_tooltip(tooltip, mgr) + if mgr.current_service.dbus.path ~= "/invalid" then + local service = mgr.current_service + local msg = tostring(service.Name) + if service.Type == "wifi" and show_signal[service.State] then + msg = msg .. "::" .. service.Strength .. "%" + end + tooltip:set_text(msg) + else + tooltip:set_text(mgr.State) + end +end + +local function get_status_icon(mgr) + if mgr.State == "offline" then + return build_icon_path(icon_statuses.unspecified.offline) + elseif mgr.State == "idle" then + return build_icon_path(icon_statuses.unspecified.idle) + elseif mgr.current_service then + local service = mgr.current_service + return service_types[service.Type](service) + else + return build_icon_path(icon_statuses.unspecified.err) + end +end + +local widget = wibox.widget.imagebox() +widget.tooltip = awful.tooltip({ objects = { widget },}) +widget.gui_client = "econnman-bin" + +function widget:update(mgr) + self:set_image(get_status_icon(mgr)) + update_tooltip(self.tooltip, mgr) +end + +widget:buttons(awful.util.table.join( + awful.button({ }, 3, + function () + spawn_with_shell(widget.gui_client) + end +))) + +local function get_manager() + return Manager:init() +end + +local status, manager = pcall(get_manager) + +if not status then + naughty.notify({preset=naughty.config.presets.critical, + title="Could not initialize connman", + text=manager}) + return widget +end + +widget:update(manager) + +cdbus.add_match("system", "type=signal,interface=" .. manager.dbus.interface) + +cdbus.add_match( + "system", + "type=signal".. + ",interface=" .. manager.current_service.dbus.interface .. + ",path=" .. manager.current_service.dbus.path .. + ",member=PropertyChanged") + +cdbus.connect_signal(manager.current_service.dbus.interface, + function (info) + if info.member == "PropertyChanged" and info.path == manager.current_service.dbus.path then + -- Strength is uint8 but Awesome returns a string + -- so I cannot use the name/value pair passed to the function. + -- Instead, I have to update all services again :-( + manager:update_services() + widget:update(manager) + end +end) + +cdbus.connect_signal( + manager.dbus.interface, + function (info) + -- for some reasone Awesome does not return the object path + -- of the services with the signal but it sets it to nil :-( + if info.member == "ServicesChanged" then + local path_before = manager.current_service.dbus.path + manager:update_services() + local path_after = manager.current_service.dbus.path + if path_before ~= path_after then + cdbus.remove_match( + "system", + "type=signal,interface=" .. + manager.current_service.dbus.interface .. ",path=" .. + path_before .. ",member=PropertyChanged") + + cdbus.add_match( + "system", + "type=signal,interface=" .. + manager.current_service.dbus.interface .. ",path=" .. + path_after .. ",member=PropertyChanged") + end + elseif info.member == "PropertyChanged" then + manager:update_properties() + end + widget:update(manager) +end) + +return widget