{"id":1709,"date":"2015-10-26T07:10:04","date_gmt":"2015-10-26T13:10:04","guid":{"rendered":"http:\/\/dev.iachieved.it\/iachievedit\/?p=1709"},"modified":"2020-06-25T13:19:53","modified_gmt":"2020-06-25T18:19:53","slug":"exploring-networkmanager-d-bus-systemd-and-raspberry-pi","status":"publish","type":"post","link":"https:\/\/dev.iachieved.it\/iachievedit\/exploring-networkmanager-d-bus-systemd-and-raspberry-pi\/","title":{"rendered":"Exploring NetworkManager, D-Bus, systemd, and Raspberry Pi"},"content":{"rendered":"<p>I had to create a new category named <i>Hacking<\/i> for this post. The end result is a Raspberry Pi outfitted with LEDs that will inform me of which network interfaces are activated. If you want to actually recreate what I&#8217;ve done you&#8217;re going to need:<\/p>\n<ul>\n<li>a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Raspberry_Pi\">Raspberry Pi<\/a> with <a href=\"https:\/\/wiki.debian.org\/DebianJessie\">Debian Jessie<\/a>\n<li>two LEDs and wiring to connect them to the GPIO pins on the Pi\n<li>a Wireless USB Adapter (I&#8217;m using a <a href=\"http:\/\/support.netgear.com\/product\/WG111v3\">NETGEAR WG111v3<\/a>)\n<li>an Ethernet connection to the Pi\n<\/ul>\n<p>A few days ago I was researching on Google and started off with this question: <a href=\"https:\/\/www.google.com\/?gws_rd=ssl#q=how+do+i+get+notified+if+a+linux+network+connection+goes+down%3F\">How do I get notified if a Linux network connection goes down?<\/a> The top result on StackOverflow led me down the path of learning about technologies that, quite frankly, I hadn&#8217;t explored before. Those being:<\/p>\n<ul>\n<li><a href=\"http:\/\/www.freedesktop.org\/wiki\/Software\/dbus\/\">D-Bus<\/a>\n<li><a href=\"https:\/\/wiki.gnome.org\/Projects\/NetworkManager\">NetworkManager<\/a>\n<li><a href=\"http:\/\/www.freedesktop.org\/wiki\/Software\/systemd\/\">Systemd<\/a>\n<\/ul>\n<p>Call me old-school, but I teethed on <a href=\"https:\/\/en.wikipedia.org\/wiki\/MkLinux\">MkLinux<\/a> and RedHat before there was a distribution called Fedora. <code>\/etc\/sysconfig\/network-scripts<\/code> and <code>\/etc\/init.d\/network<\/code> were the only game in town, and I simply hadn&#8217;t kept up with the latest in Linux-land.<\/p>\n<h2>Getting Started<\/h2>\n<p>You&#8217;re going to need a Raspberry Pi with Debian Jessie (or rather, Raspbian that is based upon Jessie). Head on over to the <a href=\"https:\/\/www.raspberrypi.org\/downloads\/raspbian\/\">Raspbians download page<\/a> and grab the release based upon Jessie. Hopefully this isn&#8217;t your first Raspberry rodeo, but if it is, once the <code>.zip<\/code> file is downloaded, go ahead and <code>unzip 2015-09-24-raspbian-jessie.img.zip<\/code> and then insert an SD card into your computer, determine what device it was associated with (hint: <code>dmesg<\/code>), and then:<\/p>\n<pre>        \ndd if=2015-09-24-raspbian-jessie.img of=\/dev\/DEVICE bs=8M        \n<\/pre>\n<p>Once the <code>dd<\/code> is complete, insert the SD card into your Raspberry Pi SD slot, connect it to your home router via Ethernet, and power it on. It <i>should<\/i> be visible shortly via Avahi and you can log in with:<\/p>\n<pre>        \nssh pi@raspberrypi.local         \n<\/pre>\n<p>As usual, the default password for the <code>pi<\/code> user is <code>raspberry<\/code>. Verify that you are indeed running Raspbian Jessie:<\/p>\n<pre>        \npi@raspberrypi ~ $ lsb_release -a        \nNo LSB modules are available.        \nDistributor ID: Raspbian         \nDescription: Raspbian GNU\/Linux 8.0 (jessie)         \nRelease: 8.0         \nCodename: jessie         \n<\/pre>\n<p>From here&#8217;s on I&#8217;m going to drop to the <code>root<\/code> user, so if a command fails due to lack of permissions, either <code>sudo su<\/code> or prepend the command with <code>sudo<\/code>. If you balk at running as root, see <a href=\"http:\/\/www.garyshood.com\/root\/\">this post<\/a> explaining why you&#8217;re a wimp.<br \/>\nBefore proceeding, you might want to also run the following on your Pi:<\/p>\n<pre>        \napt-get -y update && apt-get -y upgrade      \n<\/pre>\n<p>to get the latest and greatest packages for Raspbian.<\/p>\n<h2>Installing NetworkManager<\/h2>\n<p>We want to manage our network interfaces with NetworkManager, so we need to install it. Now, the question is: <i>why<\/i> do we want to manage our network interface with NetworkManager? The answer is to take advantage of the messages NetworkManager places on the D-Bus when the status of &#8220;network&#8221; changes. After working with it a bit, I like to think of D-Bus as the Linux equivalent of the iOS and OS X <a href=\"https:\/\/developer.apple.com\/library\/mac\/documentation\/Cocoa\/Reference\/Foundation\/Classes\/NSNotificationCenter_Class\/\">NSNotificationCenter<\/a>. When I want to listen for broadcasted events I subscribe to be notified when said events occur.<br \/>\nLet&#8217;s install NetworkManager then with:<\/p>\n<pre>        \napt-get install -y network-manager       \n<\/pre>\n<p>While NetworkManager is installing, watch the console output. You&#8217;ll see at some point:<\/p>\n<pre>        \nSetting up network-manager (0.9.10.0-7) ...      \nThe following network interfaces were found in \/etc\/network\/interfaces       \nwhich means they are currently configured by ifupdown:       \n- eth0       \n- wlan0      \n- wlan1      \nIf you want to manage those interfaces with NetworkManager instead       \nremove their configuration from \/etc\/network\/interfaces.         \n<\/pre>\n<p>Now, this information is key. We <i>are<\/i> going to go into <code>\/etc\/network\/interfaces<\/code> and remove a lot of information. Before that, let&#8217;s <a href=\"https:\/\/wiki.debian.org\/NetworkManager\">read a bit<\/a> about how NetworkManager determines whether or not it (rather than something else), is going to manage interfaces.<br \/>\nOkay, three things to do to enable NetworkManager for our interfaces:<\/p>\n<ul>\n<li>remove interface entries from <code>\/etc\/network\/interfaces<\/code>\n<li>set <code>managed=true<\/code> in <code>\/etc\/NetworkManager\/NetworkManager.conf<\/code>\n<li><code>\/etc\/init.d\/network-manager restart<\/code>\n<\/ul>\n<p>After this our files look like:<\/p>\n<pre>        \nroot@raspberrypi:\/home\/pi# cat \/etc\/network\/interfaces       \nsource-directory \/etc\/network\/interfaces.d       \nauto lo      \niface lo inet loopback       \n<\/pre>\n<pre>        \nroot@raspberrypi:\/home\/pi# cat \/etc\/NetworkManager\/NetworkManager.conf       \n[main]       \nplugins=ifupdown,keyfile         \n[ifupdown]       \nmanaged=true         \n<\/pre>\n<p>And then:<\/p>\n<pre>        \nroot@raspberrypi:\/home\/pi# systemctl restart NetworkManager      \nroot@raspberrypi:\/home\/pi# systemctl status NetworkManager       \nNetworkManager.service - Network Manager         \n Loaded: loaded (\/lib\/systemd\/system\/NetworkManager.service; enabled)        \n Active: active (running) since Mon 2015-10-26 02:20:03 UTC; 26s ago         \n Main PID: 4342 (NetworkManager)        \n CGroup: \/system.slice\/NetworkManager.service        \n   4342 \/usr\/sbin\/NetworkManager --no-daemon         \n<\/pre>\n<p>You will see in the systemd logs that NetworkManager is struggling with <code>wlan0<\/code>. We&#8217;ll fix that momentarily, but first, take a look at your interfaces with <code>nmcli<\/code> (NetworkManager CLI):<\/p>\n<pre>        \nroot@raspberrypi:\/home\/pi# nmcli device status       \nDEVICE TYPE     STATE       CONNECTION       \neth0   ethernet connected   eth0         \nwlan0  wifi     unavailable --       \nlo     loopback unmanaged   --       \n<\/pre>\n<p>So far so good. Our Ethernet device is up and connected (which if it weren&#8217;t we would have gotten knocked out of our ssh session).<\/p>\n<h2>Configuring WiFi<\/h2>\n<p>I will confess that this next part took a little trial and error, but hey, that&#8217;s what hacking is all about. Without ever having been exposed to NetworkManager one expects to fiddle with the parameters a bit to make things work. Here we go:<\/p>\n<pre>        \nroot@raspberrypi:\/home\/pi# nmcli con add con-name HomeOffice ifname wlan0 type wifi ssid MYSSID      \nConnection 'HomeOffice' (a6ce46a0-c69c-4e4a-8065-43d665702627) successfully added.       \nroot@raspberrypi:\/home\/pi# nmcli con modify HomeOffice wifi-sec.key-mgmt wpa-psk         \nroot@raspberrypi:\/home\/pi# nmcli con modify HomeOffice wifi-sec.psk MYPSK        \n<\/pre>\n<p>Of course, replace <code>MYSSID<\/code> and <code>MYPSK<\/code> with your own SSID and pre-shared key.<\/p>\n<p>All of this was sorted out by reading the <a href=\"https:\/\/access.redhat.com\/documentation\/en-US\/Red_Hat_Enterprise_Linux\/7\/html\/Networking_Guide\/sec-Using_the_NetworkManager_Command_Line_Tool_nmcli.html\">RedHat documentation<\/a>.<br \/>\nBefore enabling the connection, kill any <code>wpa_supplicant<\/code> process that may have been hanging out.<\/p>\n<pre>        \nroot@raspberrypi:\/home\/pi# ps aux|grep wpa       \nroot 2137 0.2 0.8 7116 1516 ? Ss 02:10 0:02 \/sbin\/wpa_supplicant -s -B -P \/run\/wpa_supplicant.wlan0.pid -i wlan0 -D nl80211,wext -c \/etc\/wpa_supplicant\/wpa_supplicant.conf      \nroot@raspberrypi:\/home\/pi# kill -9 2137      \n<\/pre>\n<p>and then start the connection with <code>nmcli<\/code>:<\/p>\n<pre>        \nroot@raspberrypi:\/home\/pi# nmcli con up HomeOffice       \nConnection successfully activated (D-Bus active path: \/org\/freedesktop\/NetworkManager\/ActiveConnection\/3)        \n<\/pre>\n<p>Using <code>nmcli dev status<\/code> once more:<\/p>\n<pre>        \nroot@raspberrypi:\/home\/pi# nmcli dev status      \nDEVICE TYPE     STATE     CONNECTION         \neth0   ethernet connected eth0       \nwlan0  wifi     connected HomeOffice         \nlo     loopback unmanaged --         \n<\/pre>\n<p>Very handy indeed.<\/p>\n<h2>D-Bus Messages and python-networkmanager<\/h2>\n<p>Okay, now we&#8217;re getting somewhere!  Another new command.  On your Pi, run <code>busctl monitor org.freedesktop.NetworkManager<\/code>.  You might see something like:<\/p>\n<pre>\nType=signal  Endian=l  Flags=1  Version=1  Priority=0 Cookie=4013\n  Sender=:1.21  Path=\/org\/freedesktop\/NetworkManager\/AccessPoint\/1  Interface=org.freedesktop.NetworkManager.AccessPoint  Member=PropertiesChanged\n  UniqueName=:1.21\n  MESSAGE \"a{sv}\" {\n    ARRAY \"{sv}\" {\n        DICT_ENTRY \"sv\" {\n            STRING \"Strength\";\n            VARIANT \"y\" {\n                BYTE 97;\n            };\n        };\n    };\n  };\n<\/pre>\n<p>Now, this is neat!  It <i>looks<\/i> like every time the signal strength of the WiFi connection changes there&#8217;s a message on the D-Bus.  What happens if you pull the WiFi adapter out altogether?  Two signals get emitted:  <code>AccessPointRemoved<\/code> and <code>DeviceRemoved<\/code>, along with additional signals on other paths.<\/p>\n<pre>\n Type=signal  Endian=l  Flags=1  Version=1  Priority=0 Cookie=4063\n  Sender=:1.21  Path=\/org\/freedesktop\/NetworkManager\/Devices\/2  Interface=org.freedesktop.NetworkManager.Device.Wireless  Member=AccessPointRemoved\n  UniqueName=:1.21\n  MESSAGE \"o\" {\n    OBJECT_PATH \"\/org\/freedesktop\/NetworkManager\/AccessPoint\/2\";\n  };\n\n  Type=signal  Endian=l  Flags=1  Version=1  Priority=0 Cookie=4068\n  Sender=:1.21  Path=\/org\/freedesktop\/NetworkManager  Interface=org.freedesktop.NetworkManager  Member=DeviceRemoved\n  UniqueName=:1.21\n  MESSAGE \"o\" {\n    OBJECT_PATH \"\/org\/freedesktop\/NetworkManager\/Devices\/2\";\n  };\n<\/pre>\n<p>Now, this isn&#8217;t necessarily a D-Bus tutorial or complete NetworkManager tutorial, but, you might want to read up on the NetworkManager D-Bus API <a href=\"https:\/\/developer.gnome.org\/NetworkManager\/unstable\/spec.html\">here<\/a>.<\/p>\n<p>Recall that the original question that started me down this hacking journey was how to get notified about changes in network availability.  Well, reading D-Bus messages from NetworkManager is the answer, but before we tie everything together we&#8217;ll install a Python library that is going to make this much easier.  That library is <a href=\"https:\/\/github.com\/seveas\/python-networkmanager\">python-networkmanager<\/a>.<\/p>\n<pre>\nroot@raspberrypi:\/home\/pi# easy_install python-networkmanager\n<\/pre>\n<p><code>python-networkmanager<\/code> documentation is a little sparse, but much of the functionality can be gleaned from the <a href=\"https:\/\/github.com\/seveas\/python-networkmanager\/tree\/master\/examples\">examples<\/a>.<\/p>\n<h2>Signals We&#8217;re Interested In<\/h2>\n<p>After a little trial and error I determined that we&#8217;re interested in two NetworkManager signals:<\/p>\n<ul>\n<li><code>DeviceAdded<\/code>\n<li><code>DeviceRemoved<\/code>\n<\/ul>\n<p><i>and<\/i> we&#8217;re interested in the <code>StatusChanged<\/code> signal for a given device.<\/p>\n<p>This is important!  The top-level <code>StatusChanged<\/code> signal for the NetworkManager is the <i>overall<\/i> status, and we are interested specifically in status changes for each device.<\/p>\n<p>With <code>python-networkmanager<\/code> we code this as follows:<\/p>\n<pre>\nDevices = {}\n\ndef main():\n    NetworkManager.NetworkManager.connect_to_signal('DeviceAdded',\n                                                    device_add_remove,\n                                                    **d_args)\n    NetworkManager.NetworkManager.connect_to_signal('DeviceRemoved',\n                                                    device_add_remove,\n                                                    **d_args)\n\n    for dev in NetworkManager.NetworkManager.GetDevices():\n        dev.connect_to_signal('StateChanged', device_state_change, **d_args)\n        connectionType = type(dev.SpecificDevice()).__name__\n        Devices[dev.object_path] = {}\n        Devices[dev.object_path][\"type\"] = connectionType\n        if connectionType == \"Wired\" and \\\n                NetworkManager.const('device_state',dev.State) == \"activated\":\n          Devices[dev.object_path][\"active\"] = True\n          ethernet(True)\n        if connectionType == \"Wireless\" and \\\n                NetworkManager.const('device_state',dev.State) == \"activated\":\n          Devices[dev.object_path][\"active\"] = True\n          wifi(True)\n<\/pre>\n<p>First, we &#8220;connect&#8221; (or subscribe) to the <code>DeviceAdded<\/code> and <code>DeviceRemoved<\/code> signals.  The second argument to <code>connect_to_signal<\/code> is a callback, which we&#8217;ll define later on.  Next, we use the <code>GetDevices()<\/code> method to give us all of the current devices.<\/p>\n<p><i>For each device<\/i> we connect to the <code>StateChanged<\/code> signal.  This is how we&#8217;ll know whether there was a state change for that specific device.  Then, using the <code>python-networkmanager<\/code> API we get the type of connection (Wired, Wireless, etc.), and determine whether NetworkManager reports the connection as <i>activated<\/i> (i.e., up and with an address).  If all is well we stash this information in our <code>Devices<\/code> table and call something like <code>ethernet(True)<\/code> (more on this later).<\/p>\n<p>Now, for a look at our add\/remove and state change functions:<\/p>\n<pre>\ndef device_add_remove(*args, **kwargs):\n    msg = kwargs['d_member']\n    if msg == \"DeviceAdded\":\n        # Argument will be the device, which we want to monitor now\n        args[0].connect_to_signal('StateChanged', device_state_change, **d_args)\n        return\n\n    if msg == \"DeviceRemoved\":\n        print \"Device removed:  %s\" % (args[0].object_path)\n        if args[0].object_path in Devices:\n            del args[0].object_path\n\ndef device_state_change(*args, **kwargs):\n    msg  = kwargs['d_member']\n    path = kwargs['d_path']\n\n    print \"Device state change:  %s\" % (path)\n    print \"Old state:  %s\" % NetworkManager.const('device_state', args[1])\n    print \"New state:  %s\" % NetworkManager.const('device_state', args[0])\n\n    device   = NetworkManager.Device(path)\n    newState = NetworkManager.const('device_state', args[0])\n\n    try:\n        connectionType = type(device.SpecificDevice()).__name__\n    except:\n        # D-Bus likely doesn't know about the device any longer,\n        # this is typically a removable Wifi stick\n        path = kwargs['d_path']\n        if path in Devices:\n            connectionType = Devices[path][\"type\"]\n\n    if newState == \"activated\":\n        path = kwargs['d_path']\n        print \"Device activated:  %s\" % (path)\n        Devices[path] = {\"type\":  connectionType,\n                         \"active\": True}\n        if connectionType == \"Wired\":\n          ethernet(True)\n        if connectionType == \"Wireless\":\n          wifi(True)\n    else:\n        if connectionType == \"Wired\":\n          ethernet(False)\n        if connectionType == \"Wireless\":\n          wifi(False)\n<\/pre>\n<p>Work through this code on your own; hopefully it isn&#8217;t too obtuse, but to be fair, none of this will run without filling in the gaps.  Like, what does <code>ethernet<\/code> do?  Fear not, the entire code resides in a single file called <a href=\"https:\/\/dev.iachieved.it\/iachievedit\/watchnet.py\">watchnet.py<\/a>.  Here you will find the <code>ethernet<\/code> and <code>wifi<\/code> functions, which simply raise\/lower GPIO pins.  If you have the GPIO pins connected to LEDs you get a nice visual display of what interface is up\/down at the moment.  In the first pic both LEDs are lit, thus indicating that both the Ethernet and WiFi connections are up.<\/p>\n<figure style=\"width: 242px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/10\/bothconnected2.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/10\/bothconnected2.jpg\" alt=\"bothconnected\" width=\"242\" height=\"369\" class=\"aligncenter size-full wp-image-1742\" \/><\/a><figcaption class=\"wp-caption-text\">Both Network Interfaces Connected<\/figcaption><\/figure>\n<p>In the second pic I&#8217;ve removed the Wireless USB Adapter, and the yellow LED goes out.<\/p>\n<figure id=\"attachment_1744\" aria-describedby=\"caption-attachment-1744\" style=\"width: 320px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/10\/wifidisconnected.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/10\/wifidisconnected.jpg\" alt=\"WiFi Disconnected\" width=\"320\" height=\"298\" class=\"size-full wp-image-1744\" \/><\/a><figcaption id=\"caption-attachment-1744\" class=\"wp-caption-text\">WiFi Disconnected<\/figcaption><\/figure>\n<p>I am using GPIO pins 23 and 24 on the Raspberry Pi and carrying them out to a green and yellow LED.  If you&#8217;ve never used the Pi to drive LEDs, have a look at this <a href=\"http:\/\/razzpisampler.oreilly.com\/ch03.html\">tutorial<\/a>, but realize I am using the <code>\/sys\/class\/gpio<\/code> &#8220;method&#8221; of setting the pins, and my circuit omits the resistors (I like to live dangerously).<\/p>\n<h2>Life cycle of the Wired and Wireless Devices<\/h2>\n<p>Now, I&#8217;m by no means an expert on NetworkManager and I&#8217;m sure there may be some additional states lurking in the machine that I haven&#8217;t seen, but what I can gather here is what you should expect for a &#8220;normal&#8221; sequence for both a Wired and Wireless device:<\/p>\n<p><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/10\/states.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/10\/states.png\" alt=\"states\" width=\"268\" height=\"752\" class=\"aligncenter size-full wp-image-1736\" \/><\/a><\/p>\n<p>In contrast, a Wireless device has some additional steps to obtain credentials for the connection:<\/p>\n<p><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/10\/wireless_states.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/10\/wireless_states.png\" alt=\"wireless_states\" width=\"405\" height=\"752\" class=\"aligncenter size-full wp-image-1738\" \/><\/a><\/p>\n<p>If you don&#8217;t have any LEDs lying around (what kind of hacker are you?!) run the <code>watchnet.py<\/code> script in the foreground and take note of the logs:<\/p>\n<pre>\npi@raspberrypi ~ $ .\/watchnet.py \nDevice state change:  \/org\/freedesktop\/NetworkManager\/Devices\/3\nOld state:  activated\nNew state:  unmanaged\nDevice removed:  \/org\/freedesktop\/NetworkManager\/Devices\/3\nDevice state change:  \/org\/freedesktop\/NetworkManager\/Devices\/4\nOld state:  unavailable\nNew state:  disconnected\nDevice state change:  \/org\/freedesktop\/NetworkManager\/Devices\/4\nOld state:  disconnected\nNew state:  prepare\n...\n<\/pre>\n<p>In this example the Wireless USB Adapter was pulled (Device removed, which coincidentally you will not see for pulling an Ethernet cable).<\/p>\n<p>But seriously, it&#8217;s more fun with LEDs.<\/p>\n<h2>Wrapping It Up<\/h2>\n<p>Now, to wrap everything up into a nice &#8220;no touch&#8221; environment, we turn to writing a <code>systemd<\/code> unit.  In <code>\/etc\/systemd\/system\/watchnet.service<\/code> add the contents:<\/p>\n<pre>\n[Unit]\nDescription=WatchNet\nWants=NetworkManager-wait-online.service\nAfter=NetworkManager-wait-online.service\n\n[Service]\nType=simple\nExecStart=\/home\/pi\/watchnet.py\n\n[Install]\nWantedBy=multi-user.target\n<\/pre>\n<p>Enable the service:<\/p>\n<pre>\nroot@raspberrypi:~# systemctl enable watchnet\nCreated symlink from \/etc\/systemd\/system\/multi-user.target.wants\/watchnet.service to \/etc\/systemd\/system\/watchnet.service.\n<\/pre>\n<p>Of course, make sure <code>watchnet.py<\/code> is in <code>\/home\/pi<\/code> and the execute bit (<code>chmod +x watchnet.py<\/code>) is set!<\/p>\n<p>Now, for some fun:<\/p>\n<ul>\n<li>Pull the power to the Pi\n<li>Disconnect all of the network interfaces\n<li>Choose either the Ethernet cable or Wireless USB Adapter and plug it in\n<\/ul>\n<p>The appropriate LED should light up!  Plug in the &#8220;other&#8221; device and its LED will light up as well.  So, at a glance, you can see what network interface(s) are connected on your Pi.<\/p>\n<h2>Next Time<\/h2>\n<p>I&#8217;m itching to buy a <a href=\"https:\/\/www.adafruit.com\/products\/1110\">Adafruit Character LCD<\/a> for the Pi.  Imagine displaying in text various status messages, or changing colors based upon the WiFi signal strength.  Next time!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I had to create a new category named Hacking for this post. The end result is a Raspberry Pi outfitted with LEDs that will inform me of which network interfaces are activated. If you want to actually recreate what I&#8217;ve done you&#8217;re going to need: a Raspberry Pi with Debian Jessie two LEDs and wiring [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22,19,13],"tags":[],"class_list":["post-1709","post","type-post","status-publish","format-standard","hentry","category-hacking","category-linux","category-raspberry-pi"],"_links":{"self":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/1709"}],"collection":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/comments?post=1709"}],"version-history":[{"count":46,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/1709\/revisions"}],"predecessor-version":[{"id":4143,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/1709\/revisions\/4143"}],"wp:attachment":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/media?parent=1709"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/categories?post=1709"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/tags?post=1709"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}