Linux

Air quality monitoring script for Argos (Linux) and BitBar (macOS)

2017-12-29 Bash, Linux, macOS No comments

From some time, I wanted to create my own app, which will display some data in top panel in macOS or Gnome environment on Linux. I collected some resources about that and I knew that for macOS I need to write an app in Obj-C and for Gnome I need to write a plugin in JavaScript. In both cases it requires some ceremony and preparation. Recently I’ve found a great app for macOS called BitBar (by the way it’s open-source). BitBar allows to put anything to macOS menu bar (top panel) in no time! With this project creating top panel apps is simplified to the limit. Moreover, there’s another project called Argos, which does the same thing, but for Linux with Gnome (it’s an open-source Gnome Extension).

In both cases, we just need to create a shell script, put it into appropriate directory (in case of Argos, it’s ~/.config/argos/ and in case of BitBar, we define it during the installation or first run) and then app displays our data automatically. We can also set refresh rate. E.g. if we want our script to be executed every 60 seconds, we can name it script.60s.sh. We can also create more advanced scripts and more details can be found in BitBar and Argos documentation.

In my case, I wanted to create a script, which reads CAQI (Common Air Quality Index) in my current location based on Airly sensors. Airly provides nice API, which we can use in our projects. Please remember that most of the sensors are located in Poland.

On my Ubuntu Linux with Gnome 3, I created a new script in the following path:

~/.config/argos/caqi.60s.sh

For a BitBar script location could be different. Both for BitBar and Argos follow the same naming convention.

My script looks as follows:

#!/usr/bin/env bash

CAQI=$(curl -s -X GET --header 'Accept: application/json' --header 'apikey: YOUR_API_KEY' \
    'https://airapi.airly.eu/v1/nearestSensor/measurements?latitude=YOUR_LATITUDE&longitude=YOUR_LONGITUDE&maxDistance=1000' \
    | jq .airQualityIndex | cut -f1 -d".")

MSG="Unknown"

case 1 in
  $(($CAQI <= 25)))  MSG="Great!";;
  $(($CAQI <= 50)))  MSG="Good!";;
  $(($CAQI <= 75)))  MSG="Medium";;
  $(($CAQI <= 100))) MSG="Bad";;
  $(($CAQI >= 101))) MSG="Very Bad";;
esac

echo "CAQI: $CAQI ($MSG)"

It works fine on Linux. The only requirement is to install jq. For macOS, I needed to change jq to /usr/local/bin/jq to make it work. I gathered information about air quality level from this website: https://www.airqualitynow.eu/pl/about_indices_definition.php [PL]. Of course, we need to replace API_KEY with our api key, which we can get from https://developer.airly.eu/ website as well as YOUR_LATITUDE and YOUR_LONGITUDE with coordinates of our location. It can be static location in our city. We can get them e.g. from Google Maps.

As a result, we have beautiful text in our top panel:

This screenshot was taken on Ubuntu Linux with Gnome 3. On macOS it works the same (I checked it).

Creating a Docker container with Alpine Linux including Java 8 and 9

2017-12-27 Docker, Java, Linux No comments

Recently, I’ve decided to refresh my knowledge regarding Docker and created an image with Alpine Linux and Java 9, which can be a useful base for the future projects. I used Alpine as a base image because it became quite popular in the Docker world due to its simplicity and the fact that it’s pretty lightweight when we compare it to containers based on other Linux distributions. Pure Alpine Docker container has about 4.144 MB, what is really impressing.

Container with Java 9

My Dockerfile looks pretty simple:

FROM alpine:latest
MAINTAINER pwittchen
USER root

RUN wget http://download.java.net/java/jdk9-alpine/archive/181/binaries/jdk-9-ea+181_linux-x64-musl_bin.tar.gz
RUN tar -xzvf *.tar.gz
RUN chmod +x jdk-9
RUN mv jdk-9 /usr/local/share
ENV JAVA_HOME=/usr/local/share/jdk-9
ENV PATH="$JAVA_HOME/bin:${PATH}"
RUN rm -rf *.tar.gz

We’re downloading JDK, unpacking it, moving to /usr/local/share directory, creating $JAVA_HOME environmental variable and adding $JAVA_HOME/bin to the $PATH. After that, we’re removing downloaded *.tar.gz file.

Repository with this project is available at: https://github.com/pwittchen/docker-alpine-java9
We can also find it on Docker Hub: https://hub.docker.com/r/pwittchen/alpine-java9/

To pull the image from Docker Hub, just type:

sudo docker pull pwittchen/alpine-java9

To run it with CLI, type:

sudo docker run -i -t pwittchen/alpine-java9

Then, we can play around with jshell inside the container:

/ # jshell
Dec 27, 2017 1:18:10 PM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
|  Welcome to JShell -- Version 9-ea
|  For an introduction type: /help intro

jshell> System.out.println("hello from docker!")
hello from docker!

This container is not so small and has about 919.2 MB. It contains whole JDK, so probably this size could be reduced.

Container with Java 8

I’ve also created another image with Java 8 (just in case):

FROM alpine:latest
MAINTAINER pwittchen
USER root

RUN apk update
RUN apk fetch openjdk8
RUN apk add openjdk8

We can also find it on the web:
GitHub: https://github.com/pwittchen/docker-alpine-java8
Docker Hub: https://hub.docker.com/r/pwittchen/alpine-java8/

and pull it from the Docker Hub:

sudo docker pull pwittchen/alpine-java8

and run it with CLI as follows:

sudo docker run -i -t pwittchen/alpine-java8

In this case, container has 118.5 MB, which is better result than for the previous container. In this case, we’re installing Java 8 for Alpine from official repository, so probably it’s already optimized.

I hope, you’ll find it useful while developing your projects in Java 8 or Java 9.

Using Tmux plugins with Tpm

2017-08-07 Linux, Tmux No comments

Recently, I decided to organize my Unix dotfiles in a better way. I had a few custom scripts I used in my Tmux bottom bar. I kept these scripts in .scripts directory and during installation or upgrade of my personal configuration, install.sh script was copying them from .scripts directory to /usr/local/bin/ directory. I wanted to make this configuration more solid and consistent, so I decided to transform these scripts into tmux plugins managed by tpm. I was already using a few plugins like:

In my Tmux bottom bar, I display battery level, uptime, CPU, RAM, IP number and song currently played on Spotify. Previously I just used scripts copied to /usr/local/bin/ and configuration looked like that:

set -g status-right "↑ #(showUptime) ⇅ #(showCpuUsage) ☰ #(showRamUsage) ∴ #(showIp) ↯ #{showBatteryLevel} ⧖ #(date '+%a, %b %d, %H:%M') "

I created the following plugins to replace these scripts:

In order to use Tmux plugins, we need to install Tmux Plugin Manager:

git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm

and initialize it at the bottom of our .tmux.conf file:

run '~/.tmux/plugins/tpm/tpm'

After that, it’s good to reload shell (source ~/.zshrc) and Tmux config (tmux source-file ~/.tmux.conf)

Next, we can add our plugins to .tmux.conf file:

set -g @plugin 'tmux-plugins/tmux-sidebar'
set -g @plugin 'tmux-plugins/tmux-copycat'
set -g @plugin 'tmux-plugins/tmux-pain-control'
set -g @plugin 'tmux-plugins/tmux-urlview'
set -g @plugin 'pwittchen/tmux-plugin-battery'
set -g @plugin 'pwittchen/tmux-plugin-uptime'
set -g @plugin 'pwittchen/tmux-plugin-cpu'
set -g @plugin 'pwittchen/tmux-plugin-ram'
set -g @plugin 'pwittchen/tmux-plugin-ip'
set -g @plugin 'pwittchen/tmux-plugin-spotify'

When we are in Tmux, we can install plugins by pressing prefix + I to install plugins. In my case, prefix = Ctrl+b.
After that, we can hit Enter and we’re ready to go!

Now, I could update my .tmux.conf with the variables defined by my plugins:

set -g status-right " 🔉 #{spotify_song} ↑ #{uptime} ⇅ #{cpu} ☰ #{ram} ∴ #{ip} ↯ #{battery_level} ⧖ #(date '+%a, %b %d, %H:%M') "

After this operation, I could remove custom scripts from my dotfiles and desired functionality is delivered via plugins. Moreover, anyone can install these plugins via tpm without messing with custom scripts!

screenshot from tmux after configuration

Right now, my plugins are in kind of messy state and they don’t work perfectly across all operating systems (e.g. there are problems on macOS), but they’re usable under Linux Ubuntu 16.04 LTS and it’s a good beginning for organizing mess created by the custom scripts.

That’s it! I have plans to publish another article describing how to write your custom Tmux plugin, which can be managed via tpm.

2 cool terminal emulators for Linux

2017-04-30 DSP2017, Linux, Tools 2 comments

There are many terminal emulators for Linux, but two of them acquired my attention.

Yakuake

Yakuake is a terminal emulator, which you can drop down from the top of the screen in any situation. It may look familiar to those who played Quake III, because this game has a very similar concept of the terminal for configuration purposes. It’s very convenient when we want to check something quickly. We can just press F12 do our task and press the same key to close the window.

yakuake - terminal emulator

Tilix

Tilix is another interesting terminal emulator, which is quite similar to iTerm2 for macOS. It has the possibility to create tiles and tabs just like iTerm2. It can be some kind of alternative for Tmux. Moreover, it allows searching through the console what is very useful.

tilix - terminal emulator

Do you know more interesting terminal emulators for Linux or maybe for other operating systems? Share them in comments!

Automate tile layouts creation in tmux with tmux-auto-pane

2017-04-08 Bash, DSP2017, Linux, Tmux No comments

I just released tmux-auto-pane. It’s a tiny shell script for creating pre-defined tile layouts in Tmux on Linux with xdotool.
In our workflow, we often have some pre-defined pane configurations in a terminal. The project called tmux-auto-pane helps to automate that process. It can save us some time and make us a bit more productive.

We can call tmux-auto-pane with one of the following parameters:

--help | -h   showing help
--1l1r        one left, one right
--1l2r        one left, two right
--2l1r        two left, one right
--1u1d        one up, one down
--1u2d        on up, two down
--2u1d        two up, one down
--4tiles      4 tiles, 1 in each corner

for example tmux-auto-pane --4tiles

will generate such layout:

 ____ ____
|    |    |
|____|____|
|    |    |
|____|____|

we can also have the following layouts:

    1l1r         1l2r         2l1r         1u1d        1u2d         2u1d
 ____ ____    ____ ____    ____ ____    _________    _________    ____ ____
|    |    |  |    |    |  |    |    |  |         |  |         |  |    |    |
|    |    |  |    |____|  |____|    |  |_________|  |____ ____|  |____|____|
|    |    |  |    |    |  |    |    |  |         |  |    |    |  |         |
|____|____|  |____|____|  |____|____|  |_________|  |____|____|  |_________|

Script can be installed via wget:

sh -c "$(wget https://raw.githubusercontent.com/pwittchen/tmux-auto-pane/master/install.sh -O -)"

or via curl:

sh -c "$(curl -fsSL https://raw.githubusercontent.com/pwittchen/tmux-auto-pane/master/install.sh)"

Due to the fact, that tmux-auto-pane uses xdotool under the hood, unfortunately it works only with Linux right now.
It can be improved in the future to work with macOS as well.

Source of the project can be found at https://github.com/pwittchen/tmux-auto-pane.
This project could be extended to start specific applications in each pane.
Maybe, I’ll improve it in the future, so users could parametrize their custom applications.

I hope Tmux & Linux users will find it useful :).

Browsing directories with Vim

2017-04-02 DSP2017, Linux, Vim 2 comments

I’m still learning Vim every day. At first, it looks quite hard and most of the people want to learn how to quit it and never use again. Nevertheless, when you learn some basics, you can be really productive. This editor has much more cool functionalities than just :q shortcut ;-).
Recently I discovered, you can use Vim not only for editing files but you can use it for browsing directories as well! Just go to any directory you want in terminal and type:

vi .

Then you have very nice file browser:

You can use most of the regular Vim commands like /yourkeyword for searching.

You can also navigate up and down with j and k as inside the files. To enter directory, just press Enter You also have other features like - key for going up, s for sorting, R for renaming and D for deleting. To quit this browser, just type :q (the same shortcut as in the editor).

I know, this file browsing functionality is not perfect, but it’s much better than regular navigation and directory browsing in the console with cd, ls and searching with grep. In some cases it may be a perfect choice!

Working with different Git configs

2017-03-10 DSP2017, Git, Linux, macOS No comments

Short introduction

Sometimes people need to specify multiple values for single .gitconfig file or they want to share just part of the configuration between two machines. There are different approaches for that. I can show you mine.

Different configs for different Operating Systems

On my private computer, I use Linux. I use Git for my private projects and I use my private e-mail address there. At the same time, I use Git at work on macOS with exactly the same Git configuration, but with a different e-mail address. How to deal with that?

In my .gitconfig file, I set my private e-mail address, which is used by default. In my .zshrc file, I created two aliases:

alias setupGitPersonal="git config --global user.email \"piotr@wittchen.biz.pl\""
alias setupGitForWork="git config --global user.email \"piotr.wittchen@sap.com\""

Hint: If you want to configure more stuff than just an e-mail, you can do it in the appropriate alias or you can create separate shell scripts for that and place them in /usr/local/bin/ directory.

Then, on Linux, I don’t have to do anything and my private e-mail address is used out-of-the-box.
On macOS, I do the following trick in .zshrc file:

if [ `uname` = "Darwin" ]; then
  setupGitForWork

  # rest of the macOS config goes here...
fi

After that, every time I start terminal on macOS, it automatically sets up my e-mail address to the one I use at work and keeps my .gitconfig file updated.

Hint: If you don’t use zsh, instead of .zshrc file, edit .bashrc file.

Different configs for the same OS on two machines

If you’re using different configs on the different machines with the same OS, you can try another trick. Create configuration file – e.g. .machine_name in your home directory. Setup one name on one machine and another name on a different machine. Next, include this file in your .zshrc or .bashrc file, perform appropriate check and load different settings basing on variable name.

. ~/.machine_name

if [ $machineName = "workMachine" ]; then
  setupGitForWork
else
  setupGitPersonal
fi

Contents of the .machine_name file are simple:

machineName="workMachine"

Different configs on the single machine with one OS

In such case, we are supposed to perform the manual switch. We can use aliases provided above. When we want to have personal settings, we can open terminal and type setupGitPersonal. When we want to apply work settings, then we can type setupGitForWork.

Summary

As we can see, keeping different configs for different machines or operating systems and changing them depending on our needs is not so hard. I hope these ideas will help you to manage your configs.

Control Spotify on Linux like a hacker

2017-03-05 DSP2017, Linux, Open source, Python 2 comments

Recently, I created a tiny script called spotify-cli, which allows you to control Spotify on Linux from terminal. It’s inspired by shpotify, which is a shell script doing similar things, but on macOS. My script is written in Python and uses dbus under the hood, which allows to communicate with bus daemon to pass messages between applications. I used pactl for controlling the system sound.

You can install spotify-cli as follows via wget:

sh -c "$(wget https://raw.githubusercontent.com/pwittchen/spotify-cli-linux/master/install.sh -O -)"

or via curl:

sh -c "$(curl -fsSL https://raw.githubusercontent.com/pwittchen/spotify-cli-linux/master/install.sh)"

After that, you can just type spotify-cli in your terminal.
You can use spotify-cli with the following parameters:

--help, -h          shows help
--status            shows status (currently played song name and artist)
--play              plays the song
--pause             pauses the song
--playpause         plays or pauses the song (toggles a state)
--next              plays the next song
--previous, --prev  plays the previous song
--volumeup          increases sound volume
--volumedown        decreases sound volume

That’s it! Happy listening!

Source code of the project can be found at https://github.com/pwittchen/spotify-cli-linux.

Lifting quality of a shell script

2016-11-30 Bash, Linux No comments

Introduction

In release cycle of our team at work, we need to perform so-called system tests. In order to do that, we need to log into Artifactory, search for the latest release package, check if it’s up to date, download it, unzip it, install internal configuration recipe, compile, initialize & run it. Not all of that can be easily automated, but I thought that at least searching & downloading phase can be done from the terminal in a semi-automated way. That’s why I created ydownloader shell script.

Writing a script with unit tests and continuous integration

I’m not an expert in shell scripting, so I also wanted to learn more about it. In addition, I wanted to apply best software development practices in that script. Someone can say that in the case of a simple shell script proper engineering may be a superfluity, but in my opinion, the simplicity of the project is not an excuse for doing it the right way. Especially, if we want to use it in the future. That’s why I divided this script into smaller functions, added command line arguments handling and help for the users. Moreover, I added unit tests with shunit2 (yes, we can write unit tests for the shell scripts) and continuous integration with Travis CI server. In the “Clean Code” book, we can read that code without unit tests is not clean by definition. After dividing script into smaller functions, it was much easier to test it. My script is accepting command line arguments, so I needed to do the following trick to make it testable and include it in the testing script:

if [ "$TEST_MODE" == "" ]
then
  TEST_MODE=false
fi

if [ "$TEST_MODE" = false ] ; then
  # parse command line arguments here...
else
  echo "TEST_MODE enabled"
fi

Then, I could write unit tests:

TEST_MODE=true
. ./ydownloader # load script to be tested
echo "RUNNING UNIT TESTS..."

testCutLastChars() {
  # given
  valueToCut='testString'
  expectedValue='testStri'

  # when
  actualValue=$(echo $valueToCut | cutLastChars 3)

  # then
  assertEquals $expectedValue $actualValue
}

# more tests goes here...
. ./shunit2/shunit2 # load shunit2

There are also other solutions for unit testing like bats, assert.sh and others. We can choose what we like. We can also use additional tools like shunit2-colorize to make our console output of shunit2 tests look like a rainbow if we are not fans of monochromatic terminal. Moreover, we can use static code analysis tools for shell scripts like shellcheck.

In addition, I prepared simple install script, which allows to install script locally via curl or wget. Of course, project has sufficient documentation in README.md file.

Short recap

It was really nice coding exercise. Now, I feel much more comfortable with shell scripting, but there’s still a lot to learn. I recommend trying applying a similar approach in your scripts if you haven’t done it yet.

If you want to browse complete project, check it out in my repository: https://github.com/pwittchen/ydownloader.

Converting audio CD to *.mp3 files on Linux

2016-01-24 Linux, Ubuntu 2 comments

Not all songs are available on Spotify and sometimes we need to rip music from audio CDs in order to listen it on our computer or mobile device.

There are several approaches to do that. Here is mine:

Get RipperX:

sudo apt-get install ripperx

Put audio CD into your computer. Open RipperX, select All Tracks and check Rip to WAV option. Set quality of output files via “Config” option and names of the tracks if your want. Press “Go!”. After conversion, you should have directory with ripped *.wav files in your home directory.

Get SoundConverter:

sudo apt-get install soundconverter

Run it and convert *.wav files to *.mp3. You can choose another output format if you want.

Get EasyTag:

sudo add-apt-repository ppa:amigadave/ppa
sudo apt-get update
sudo apt-get install easytag

Open EasyTag and set appropriate tags for your *.mp3 files. You can also set CD cover as image for each file.

You are done!

Now, you can import files to your favorite music player. I’m using Spotify right now, where I can select local files, create a playlist and even sync it with my mobile device if my computer is in the same WiFi network. It’s very handy option.

I hope, this short tutorial will be helpful for you.