Raspberry Pi System Information Web Application with Python and Flask

Ashok Raja
 
Solutions Architect
March 14, 2016
 
Rate this article
 
Views
3705

Although there are Android Apps to view the system information of Raspberry Pi, accessing those information outside of your network (remotely) requires additional plumbing work if your home broadband doesn’t support port forwarding. This article provides details on creating a simple Python application to gather system information of Raspberry pi and serve it as a web application with Flask. In the next article I’ll explain how to access this application outside of the home network.

Development Tools

Due to limited system resources of Raspberry Pi, using it as full blown web application development environment would be a time consuming activity. So I did the initial development in a Debian VM running on Virtual Box with PyCharm Community Edition IDE and then ported it to the Raspberry Pi environment. Enabling debugging in Flask would help to idetify or trouble shoot issues that are encountered after deployment to Raspberry Pi

Final Output

The below is the final out that you can expect in Raspberry Pi

image

Linux Variants

As an usual non-Linux user, I started with Ubuntu, but running it as a VM on Windows 8.1 or Windows 10 doesn’t seems to be stable in Virtual Box, particularly the Full Screen Mode always caused the VM to crash. So I opted for Debian which is the most trusted Server variant of Linux. Raspian OS is yet another Debian variant and this is also one of the reason I opted for Debian. BTW, Ubuntu is also one of the hundreds !!! of Debian variants. The below is the partial list of Debian Linux Variants from wikipedia.

clip_image004

Get the full list of Linux Variants from Wikipedia

Python Flask Application

I started this application sometime back by using psutils module for Python. During the course of development, I came to the conclusion that psutil is an overkill for this application and re-wrote those features with native Linux and Raspberry Pi commands. Except Flask, this application does not require any additional external Python Modules. The below is the solution structure of the project in PyCharm.

clip_image005

Index.py

This file contains the entire Python code for fetching the system information and converting it into Flask objects. Apart from getting the system information, from a developer perspective this file also has examples for

1. Routes in Flask

2. Custom Functions in Python

3. Flask Context Processor ( Direct invocation of Python Functions from HTML)

4. Flask Utility Processor ( Data Formatting Extensions, like filters in Angular Js)

5. Invoking Linux Terminal Commands from Python Code


 from flask import Flask, render_template
 from datetime import datetime
 import platform
 import subprocess
 
 app = Flask(__name__)
 
 @app.route('/')
 @app.route('/home')
 def index():
     sys_data = {"current_time": '',"machine_name": ''}
     try:
         sys_data['current_time'] = datetime.now().strftime("%d-%b-%Y , %I : %M : %S %p")
         sys_data['machine_name'] =  platform.node()
         cpu_genric_info = cpu_generic_details()
         disk_usage_info = disk_usage_list()
         running_process_info = running_process_list()
     except Exception as ex:
         print ex
     finally:
         return render_template("index.html", title='Raspberry Pi - System Information',
                                sys_data = sys_data,
                                cpu_genric_info = cpu_genric_info,
                                disk_usage_info= disk_usage_info,
                                running_process_info = running_process_info)
 
 
 def cpu_generic_details():
     try:
         items = [s.split('\t: ') for s in subprocess.check_output(["cat /proc/cpuinfo  | grep 'model name\|Hardware\|Serial' | uniq "], shell=True).splitlines()]
     except Exception as ex:
         print ex
     finally:
         return items
 
 @app.context_processor
 def boot_info():
     item = {'start_time': 'Na','running_since':'Na'}
     try:
         item['running_duration'] = subprocess.check_output(['uptime -p'], shell=True)
         item['start_time'] = subprocess.check_output(['uptime -s'], shell=True)
     except Exception as ex:
         print ex
     finally:
         return dict(boot_info = item)
 
 
 @app.context_processor
 def memory_usage_info():
     try:
         item = {'total': 0,'used': 0,'available': 0 }
         item['total']=  subprocess.check_output(["free -m -t | awk 'NR==2' | awk '{print $2'}"], shell=True)
         item['used']=  subprocess.check_output(["free -m -t | awk 'NR==3' | awk '{print $3'}"], shell=True)
         item['available']= int(item['total'])- int(item['used'])
     except Exception as ex:
         print ex
     finally:
         return dict(memory_usage_info = item)
 
 
 @app.context_processor
 def os_name():
     os_info = subprocess.check_output("cat /etc/*-release | grep PRETTY_NAME | cut -d= -f2", shell=True).replace('\"', '')
     return dict(os_name=os_info)
 
 
 @app.context_processor
 def cpu_usage_info():
     item = {'in_use': 0}
     try:
         item['in_use'] = subprocess.check_output("top -b -n2 | grep 'Cpu(s)'|tail -n 1 | awk '{print $2 + $4 }'", shell=True)
     except Exception as ex:
         print ex
     finally:
         return dict(cpu_usage_info = item)
 
 
 @app.context_processor
 def cpu_processor_count():
     proc_info = subprocess.check_output("nproc", shell=True).replace('\"', '')
     return dict(cpu_processor_count=proc_info)
 
 
 @app.context_processor
 def cpu_core_frequency():
     core_frequency = subprocess.check_output("vcgencmd get_config arm_freq | cut -d= -f2", shell=True).replace('\"', '')
     return dict(cpu_core_frequency=core_frequency)
 
 
 @app.context_processor
 def cpu_core_volt():
     core_volt = subprocess.check_output("vcgencmd measure_volts| cut -d= -f2", shell=True).replace('\"', '')
     return dict(cpu_core_volt=core_volt)
 
 
 @app.context_processor
 def cpu_temperature():
     cpuInfo = {'temperature': 0, 'color': 'white'}
     try:
         cpuTemp = float(subprocess.check_output(["vcgencmd measure_temp"], shell=True).split('=')[1].split('\'')[0])
         cpuInfo['temperature']=cpuTemp
         if cpuTemp > 40 and cpuTemp < 50:
             cpuInfo['color'] = 'orange'
         elif cpuTemp > 50:
             cpuInfo['color'] = 'red'
         return cpuInfo
     except Exception as ex:
         print ex
     finally:
         return dict(cpu_temperature=cpuInfo)
 
 def disk_usage_list():
     try:
         items = [s.split() for s in subprocess.check_output(['df', '-h'], universal_newlines=True).splitlines()]
     except Exception as ex:
         print ex
     finally:
         return items[1:]
 
 def running_process_list():
     try:
         items = [s.split() for s in subprocess.check_output(["ps -Ao user,pid,pcpu,pmem,comm,lstart --sort=-pcpu"], shell=True).splitlines()]
     except Exception as ex:
         print ex
     finally:
         return items[1:]
 
 
 @app.context_processor
 def utility_processor():
     def short_date(a,b,c):
         return u'{0}{1}, {2}'.format(a, b,c)
     return dict(short_date=short_date)
 
 
 if __name__ == "__main__":
     app.run(debug=True)
 

Index.html

This HTML file is more a template, which binds the data that this generated in index.py. You can see some similarities between Angular Js and Flask data binding as both uses curly braces to identify the model. For responsive UI, I have used Skeleton instead of bootstrap as this application doesn’t require any complex HTML components. Table sorting is done with the help of sorttable Js


 <!DOCTYPE html>
 <html lang="en">
 <head>
     <!-- Basic Page Needs
     –––––––––––––––––––––––––––––––––––––––––––––––––– -->
     <meta charset="utf-8">
     <title>{{title}}</title>
     <meta name="description" content="System information of Raspberry Pi">
     <meta name="author" content="Ashok Raja T">
     <!-- Mobile Specific Metas
     –––––––––––––––––––––––––––––––––––––––––––––––––– -->
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <!-- FONT  –––––––––––––––––––––––––––––––––––––––––––––––––– -->
     <link href="//fonts.googleapis.com/css?family=Raleway:400,300,600" rel="stylesheet" type="text/css">
     <!-- CSS
     –––––––––––––––––––––––––––––––––––––––––––––––––– -->
     <link rel="stylesheet" href="static/css/normalize.css">
     <link rel="stylesheet" href="static/css/skeleton.css">
     <link rel="stylesheet" href="static/css/pi.css">
     <script src="static/js/sort.js"></script>
     <!-- Favicon
     –––––––––––––––––––––––––––––––––––––––––––––––––– -->
     <link rel="icon" type="image/png" href="static/images/favicon.png">
     <style>
 
     </style>
 </head>
 <body style="background-color:#757575">
     <!-- Primary Page Layout
     –––––––––––––––––––––––––––––––––––––––––––––––––– -->
     <div class="container" style="max-width:600px;color:#ffffff">
         <div class="row" style="padding-top:20px;">
             <div class="two columns">
                 <img src="static/images/logo.png" style="width:60px" />
             </div>
             <div class="ten columns">
                 <h3 style="margin-top:10px;color:#FF8C00">Raspberry Pi - Sys Info</h3>
             </div>
         </div>
               <div class="row g-header">
             <h5>
                 Generic Info
             </h5>
         </div>
         <div class="row g-row">
             <div class="three columns g-head">
                 Name of the Pi
             </div>
             <div class="nine columns">
                 {{sys_data.machine_name}}
             </div>
         </div>
         <div class="row g-row">
             <div class="three columns g-head">
                 Hardware Type
             </div>
             <div class="nine columns">
                 {{cpu_genric_info[1][1]}}
             </div>
         </div>
         <div class="row g-row">
             <div class="three columns g-head">
                 Serial Number
             </div>
             <div class="nine columns">
                 {{cpu_genric_info[2][1]}}
             </div>
         </div>
         <div class="row g-row">
             <div class="three columns g-head">
                 Operating System
             </div>
             <div class="nine columns">
                 {{os_name}}
             </div>
         </div>
         <div class="row g-row">
             <div class="three columns g-head">
                 Running Since
             </div>
             <div class="nine columns">
                 {{boot_info.running_duration}}, <b>Started on </b> {{boot_info.start_time}}
             </div>
         </div>
         <div class="row g-row">
             <div class="three columns g-head">
                 System Time
             </div>
             <div class="nine columns">
                 {{sys_data.current_time}}
                 <div style="text-align:right;padding-top:10px">
                     <a href="restart" onclick="return confirm('Do you wish to re-start your system ?')" class="button button-primary" style="margin-right:10px">Restart</a>
                     <a href="shutdown" onclick="return confirm('Do you wish to shutdown your system ?')" class="button button-primary">Shutdown</a>
                 </div>
             </div>
         </div>
                 <div class="row g-header">
             <h5>
                 CPU Details
             </h5>
         </div>
         <div class="row g-row">
             <div class="three columns g-head">
                 Processor Type
             </div>
             <div class="nine columns">
                 {{cpu_genric_info[0][1]}}
             </div>
         </div>
         <div class="row g-row">
             <div class="three columns g-head">
                 Core Frequency
             </div>
             <div class="nine columns">
                 {{cpu_core_frequency}} Mhz
             </div>
         </div>
         <div class="row g-row">
             <div class="three columns g-head">
                 No of Cores
             </div>
             <div class="nine columns">
                 {{cpu_processor_count}}
             </div>
         </div>
         <div class="row g-row">
             <div class="three columns g-head">
                 Temperature
             </div>
             <div class="nine columns" style="color:{{cpu_temperature.color}}">
                 {{cpu_temperature.temperature}} &#176; C
             </div>
         </div>
         <div class="row g-row">
             <div class="three columns g-head">
                 Core Volt
             </div>
             <div class="nine columns">
                 {{cpu_core_volt}}
             </div>
         </div>
         <div class="row g-row">
             <div class="three columns g-head">
                 CPU  Usage
             </div>
             <div class="nine columns">
                 {{cpu_usage_info['in_use']}} %
             </div>
         </div>
         <div class="row g-header">
             <h5>
                 Memory Details
             </h5>
         </div>
         <div class="row g-row">
             <div class="three columns g-head">
                 Total Memory
             </div>
             <div class="nine columns g-row">
                 {{memory_usage_info.total}} MB
             </div>
         </div>
         <div class="row g-row">
             <div class="three columns g-head">
                 In Use
             </div>
             <div class="nine columns">
                 {{memory_usage_info.used}} MB
             </div>
         </div>
         <div class="row g-row">
             <div class="three columns g-head">
                 Free
             </div>
             <div class="nine columns">
                 {{memory_usage_info.available}} MB
             </div>
         </div>
                 <div class="row g-header">
             <h5>
                 Disk Details <span class="item-count"> ( {{ disk_usage_info | length}} ) </span>
             </h5>
         </div>
         <div>
             <table class="u-full-width sortable">
                 <thead style="color:#C1FFC1">
                     <tr>
                         <th>File System</th>
                         <th>Size</th>
                         <th>Used</th>
                         <th>Avail</th>
                         <th>Used%</th>
                         <th>Mounted On</th>
                     </tr>
                 </thead>
                 <tbody>
                     {% for disk_item in disk_usage_info %}
                     <tr>
                         <td>{{disk_item[0]}}</td>
                         <td>{{disk_item[1]}}</td>
                         <td>{{disk_item[2]}}</td>
                         <td>{{disk_item[3]}}</td>
                         <td>{{disk_item[4]}}</td>
                         <td>{{disk_item[5]}}</td>
                     </tr>
                     {% endfor %}
                 </tbody>
             </table>
         </div>
                 <div class="row g-header">
             <h5>
                 Running Process Details <span class="item-count"> ( {{ running_process_info | length}} ) </span>
             </h5>
         </div>
         <div>
             <table class="u-full-width sortable">
                 <thead style="color:#C1FFC1">
                     <tr>
                         <th>User</th>
                         <th>ProcId</th>
                         <th>CPU%</th>
                         <th>Mem%</th>
                         <th>Application</th>
                         <th>Started On</th>
                     </tr>
                 </thead>
                 <tbody>
                     {% for procinfo in running_process_info %}
                     <tr>
                         <td>{{procinfo[0]}}</td>
                         <td>{{procinfo[1]}}</td>
                         <td>{{procinfo[2]}}</td>
                         <td>{{procinfo[3]}}</td>
                         <td>{{procinfo[4]}}</td>
                         <td nowrap>{{ short_date( procinfo[7] , procinfo[6],  procinfo[8])  }}</td>
                     </tr>
                     {% endfor %}
                 </tbody>
             </table>
         </div>
     </div>
     <!-- End Document
       –––––––––––––––––––––––––––––––––––––––––––––––––– -->
 </body>
 </html>
 

Custom CSS

Apart from the default CSS files, that are part of Skeleton CSS, the below is the CSS that have been used to make changes to UI.


 h5 {
     margin-bottom: 2px;
 }
 
 th, td {
     padding-top: 5px !important;
     padding-bottom: 5px !important;
 }
 
 .g-header {
     padding-top: 10px;
     border-bottom: 1px solid #eee;
     color: #FFD700;
 }
 
 .g-row {
     padding-top: 5px;
 }
 
 .g-head {
     font-weight: bold;
 }
 
 table.sortable thead {
     font-weight: bold;
     cursor: default;
 }
 .item-count {
     color: white;
     font-size: 75%;
 }

Running the application

Create a folder named “sysinfo” inside home folder (In my case, “sysinfo” folder is inside “www” folder of “home” folder) of Raspberry pi and place these files inside that folder. Ensure that you are following the folder structure that is required by Flask.

clip_image006

To run this application, open the Terminal window from “sysinfo” folder and type

“python index.py” ( without quotes) . If your environment is configured properly for Python and Flask, you would get a notification in the console stating that the application is running at port 5000. Now you can access the application by opening the browser and point to http://localhost:5000.

clip_image007

To access the application remotely, outside of your home WiFi, this application should be served via Apache or Nginix Webserver. In the next article we can see how to route/serve this application via Apache and remotely view the Raspberry Pi system information from outside the home network.

Category : Flask, Python, Raspberry Pi

Author Info

Ashok Raja
 
Solutions Architect
 
Rate this article
 
I am Ashok Raja, Share Point Consultant and Architect based out of Chennai, India. ...read more
 

Leave a comment