Several years ago I purchased a set of Energenie remote controlled sockets that I used to be able to switch on my living room lamps from my sofa. Since I moved, I haven't put them to much use as they're not as convenient y my current house.
However, a neighbour had a break-in this summer and that made me think I could try and find out the way to program these to switch on and off lights or other devices while we're away so I can give the impression that the house is not empty and avoid a posible burglar threat.
The same company developed a control board for Raspberry Pi (PiMote) so I thought I could use that and develop a piece of software to program them while we're away. Maybe even to action them remotely from our mobile phone or computer.
This article will cover the first step, ie, set up the Raspberry Pi as a with PiMote to pair with and action the sockets and develop a simple web application to operate the sockets from there.
Set up the Raspberry Pi
The first steps to set up the Raspberry Pi are easy to reproduce, a quick google search should be enough if you encounter any trouble:
- Download the latest Raspberry Pi OS and burn the SD card using Raspberry Pi Imager. The Lite version is enough as we do not need any graphic interface for this project.
- Plug the PiMote board to the Raspberry Pi, the power source and wait until it boots.
- Config WiFi settings or plug the Ethernet cable.
- Activate SSH to be able to connect to your RPi remotely.
Once all this is ready, we can ssh into our RPi from our computer.
Use PiMote to operate the Energenie sockets
The oficial PiMote Manual is a very good guide to learn how to operate the Energenie sockets from our RPi. I used its example to develop the python scripts for this projects. First, let's ssh into our RPi:
First, we need to pair the sockets to PiMote, for which I developed this script, heavily based on the official manual. First, create a new file:
$ sudo nano energenie_pair.py
Paste the script code and save.
Plug one of the sockets and press the switch until the LED light starts blinking. Then execute the script like this:
$ python energenie_pair.py 1
Press enter when asked and the led light of your Socket should turn off. Then, you will have paired the Socket "1" to your RPi+PiMote device. It is convenient to label your socket in order not to forget which one is which.
As you can see on this script, pairing and switching the sockets is done through a control code which is different depending on the socket and the action. In order to simplify things, there is a Python library available to deal with the RPi.GPIO details. First we install the python library:
$ sudo apt-get install python-pip
$ sudo pip install energenie
Then, the switch script is pretty easy:
Using this script, we can just switch on or off our energenie socket just by typing:
$ python energenie_switch 1 on
$ python energenie_switch 1 off
These command will switch on and off the Energenie socket paired as 1.
With this and cron, it should be enough to program lights to be switched on and off at certain times. However, this is not all we want. We want to be able to easily switch the sockets from a nice web interface and ultimately even being able to schedule programs. The next step will cover the creation of a PHP web interface and set-up of an Apache server on our RPi that will serve our GUI.
The software
Even though this problem could be solved by putting together a couple of scripts. I always tend not to go the "easy" way and just do it because I know at some point it will break. Every software project, as simple as they can be, should be tackled in an organised manner. I've been a software engineer for long enough to know the benefits of a properly structured project and the use of good practice and development techniques even for the simplest projects. For this reason, I decided to use some open source tools to lean on in order to ease the development as well as help to keep the project structured.
This instructions assume that you have your own web development environment using PHP + MySQL and related tools to develop our application. Although it would be technically possible to develop directly on the Raspberry Pi, it would not be at all advisable, plus we can explain the setup of the server and deployment of the application later, giving an idea of how a real world project works.
Slim framework
For this project, the primary tool I'll be using is the Slim PHP framework, a "a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs". It is a very simple framework, quick to learn and lean (you could understand its source code in a day). You can read more about this framwork on its official website.
Install framework using composer:
composer require slim/slim:"4.*"
Slim requires any implementation of PSR-7 interfaces for its Request and Response objects, but also has its own ones. We need to install them separately:
composer require slim/psr7
Optionally, we can use a dependency container to prepare, manage, and inject application dependencies. Slim supports containers that implement PSR-11 like PHP-DI. To install it, type:
composer require php-di/php-di
Once we have installed these, let's create a directory structure for the project:
.
├── bin/ Executable files
│ └── energenie_pair.py
│ └── energenie_switch.py
├── config/ App configuration
│ └── bootstrap.php bootstrapping the application
├── public/ Web server files (DocumentRoot)
│ └── .htaccess Apache redirect rules for the front controller
│ └── index.php The application entry point
├── src/ PHP source code
├── templates/ Twig templates
├── var/ Temporary files (cache and logfiles)
├── vendor/ External package (for composer)
├── .htaccess Internal redirect to the public/ directory
├── .env Environment variables (DB access) and config values
├── .gitignore Git ignore rules
└── composer.json Project dependencies and autoloader
And add a working PSR-4 autoloader for the src/
directory.
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
Run composer update
for the changes to take effect.
Entrypoint
Our application should have a single entry point from which the whole application should be served. We will use Apache redirections later to rewrite rules to redirect the web traffic to this single entry point to the application. First, create the application entry point in public/index.php
:
<?php
(require __DIR__ . '/../config/bootstrap.php')->run();
Configuration
In order to simplify things, we will put all our settings, config and bootstrap code in a single file, which is enough due to the simplicity of this project. If this gets too complex, we can always divide it into several files to separate concerns. In the config directory, create the bootstrap.php file:
<?php
use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;
use Slim\App;
use Slim\Factory\AppFactory;
$containerBuilder = new ContainerBuilder();
// Build PHP-DI Container instance
$container = $containerBuilder->build();
// Instantiate the app
AppFactory::setContainer($container);
$app = AppFactory::create();
// Add middleware
$app->addBodyParsingMiddleware();
// Register routes
$app->get('/', function (Request $request, Response $response) {
$response->getBody()->write('Hello world!');
return $response;
});
return $app;
With this in place, we could navigate to our home page and it should show "Hello world!".
Other dependencies
For any data that we'll need to persist, we're going to need a database. To manage the persistence service and related functionality, we will be using Doctrine ORM, the Object-Relational Mapper that is used by Symfony.
Install Doctrine ORM
composer require doctrine/orm
To use the Doctrine entity manager and provide our MySQL database credentials, we need to add the following to our bootstrap.php file:
However, this would expose our DB credentials and possibly other config to the version control system. To avoid this, we can make use of dotenv in order to abstract our credentials from our code.
Install DotEnv
composer require vlucas/phpdotenv
create .env
file in our root directory with the following values:
And edit the bootstrap.php to include the .env values:
We won't commit the .env
to our source code repository making thus our database credentials independent to our source code.
To run database migrations, validate class annotations and so on we will use the Doctrine CLI application that is already present at vendor/bin
, but in order to work, this script needs a cli-config.php
file at the root of the project telling it how to find the EntityManager
we just set up:
Lastly, we want to install Twig, a PHP flexible template engine to help us display our views:
composer require slim/twig-view
After adding these dependencies, our bootstrap file will look like this:
Now we can say, we're ready to start designing our software.
The domain
The first thing we need to model are the sockets. We will use a Doctrine entity to store on DB the following information:
- Socket ID
- Name
- Description
So we create our model class under src/Domain/Socket.php
To create this table on our DB, we just run php vendor/bin/doctrine orm:schema-tool:update
. Alternatively we can just prepare the CREATE TABLE
statement on SQL and run it using our management system (phpmyadmin, MySQL workbench, etc).
To seed the DB, we could create a migration that would input some records on our DB but the sake of simplicity, I'll just manually input the desired records on DB:
INSERT INTO sockets (`id`, `name`, `description`)
VALUES
(1, 'Living room', 'Living room mantelpiece lamp'),
(2, 'Office', 'Office desk lamp'),
(3, 'Bedroom', 'Bedroom night stand lamp');
Socket Operations (factory method)
For now, we will create three kinds of socket operations (I decided to call them 'operations' rather than 'actions' in order not to confuse them with the controller actions): 'On', 'Off' and 'Pair'. Those operations apply to any of the sockets and will execute one of the python scripts we included in the bin/
directory of our project. To model this, we can make use of an abstract class that will hold the common functionality of any operation and extend it for each of the aforementioned operations. If, in the future, we decided to add a new socket operation, we would just need to extend this class again to create the new one. Finaly we could use a class that would create the kind of operation we need depending on the case. In Software Engineering, this is called a factory method design pattern and is a nice way to show good development practices in this project.
Create the following files on src/Application/SocketOperations
:
And then, extend this for specific operations:
And finally, the factory:
In this example, I'm injecting a Configuration object that I created to hold any config info that would need to be available at any point in the application. In this case, I'm adding the bin/scripts
directory of our project so those are the scripts executed when calling a socket operation. To make this available, I added the following to our config/bootstrap.php
file:
// Configuration
Configuration::class => new Configuration([
// python scripts path
'scripts_path' => __DIR__ . '/../bin/scripts'
])
Controllers
Now we need to develop the controllers. We only need two actions for now: rendering the home page or executing an operation. Slim allows you to create controller action in several ways, such as:
- As a closure function where the route is defined (as in the bootstrap.php example).
- As a class implementing
__invoke()
method. - As a class method.
I'm going to go ahead and use the third way, as I'm sure having a controller class will be useful as the application grows. The switch action can be defined in src/Controllers/
like this:
We need to add the routes on the bootstrap.php file like so:
View
The last thing to do is to create the home (and only) page controller. For this, I'll be using Bootstrap, and JQuery to send the requests to the server. First we need to develop the controller:
And finally, a view using Twig:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="/docs/4.0/assets/img/favicons/favicon.ico">
<title>Cam House sockets</title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<style>
html,
body {
height: 100%;
}
</style>
</head>
<body class="text-center">
<div class="container d-flex h-100 p-3 mx-auto flex-column">
<header class="mb-auto">
<h3 class="brand">House Sockets</h3>
</header>
<main role="main">
<div class="alert" role="alert" style="display:none">
</div>
<table class="table">
<tbody>
{% for socket in sockets %}
<tr data-id={{ socket.id }}>
<td>Socket {{ socket.id }}</td><td>{{ socket.name }}</td><td>{{ socket.description }}</td><td><a class="btn btn-success btn-on">on</a> <a class="btn btn-danger btn-off">off</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</main>
<footer class="mt-auto">
</footer>
</div>
<!-- JS, Popper.js, and jQuery -->
<script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
<script>
$(document).ready(function() {
function switchSocket(url, socket){
$.post(url, { "socket" : socket }, null, "json")
.done(function(data) {
$alert = $('.alert')
$alert.addClass('alert-success').html(data.message).fadeIn();
setTimeout(function(){
$alert.fadeOut();
}, 2000)
})
.fail(function(data) {
$alert = $('.alert')
$alert.addClass('alert-danger').html(data.responseJSON.message).fadeIn();
setTimeout(function(){
$alert.fadeOut();
}, 2000)
});
}
$(".btn-on").click(function() {
const socketId = $(this).closest("tr").data("id")
switchSocket("/switch/on", socketId)
});
$(".btn-off").click(function() {
const socketId = $(this).closest("tr").data("id")
switchSocket("/switch/off", socketId)
});
});
</script>
</body>
</html>
With this, our web application to control the switches can be considered finished for the scope of this article.
Set up web server
Install Apache
In order to deploy our application to our Raspberry Pi, we first need to set it up as a web server. The software par excellence that allows the machine to manage user http requests is Apache, which is at the moment the most used web server with about 60% of the web using it.
Before installing Apache it is always good practice to update our machine:
$ sudo apt-get update
$ sudo apt-get upgrade
Once the Raspberry Pi is up to date, we install the Apache server
$ sudo apt-get install apache2
Once the installation is completed, we can test that Apache is working properly by going to the Raspberry Pi address on a browser, so we can open our browser and type "http://192.168.0.31". You should then get a page with a message saying "It works!" and plenty of other text.
Install PHP
We need to install the PHP interpreter in order to serve our application:
$ sudo apt install php php-mbstring
To check php is working, delete the index.html
file in /var/www/html
and then create an index.php
file:
echo "<?php phpinfo();?>" > /var/www/html/index.php
Check again "http://192.168.0.31" and we should see the PHP info page.
Install MySQL
$ sudo apt-get install mariadb-server php-mysql
To check MySQL is working we will connect via the command:
$ sudo mysql --user=root
We will now delete the default MySQL root user and create a new user, because the default one can only be used with Linux root account, and so not available for the webserver and php scripts. To do so, once we connect to MySQL, we simply run the following commands:
DROP USER 'root'@'localhost';
CREATE USER 'root'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION;
So we now have a web server, connected to PHP and MySQL.
Deployment
Copy files and add permissions
Due to the simplicity of this project, we will not set up any complex deployment strategy. To deploy our application we'll keep it simple this time: we will copy over our code files to our Raspberry Pi web directory /var/www/html
using an FTP client like Filezilla.
The Apache user www-data will be the one reading the web app so we should set it as owner and grant apropriate permissions:
$ sudo chown -R pi:www-data /var/www/html/
$ sudo chmod -R 770 /var/www/html/
Then, we need to make sure the energenie python scripts work. We can test this with Postman. Take into account that it will be the www-data
linux user who will be executing the python scripts so we need to make sure that it has the correct permissions and the scripts are executable so we need to run the following:
$ sudo chmod +x /var/www/html/bin/scripts/energenie_pair.py
$ sudo chmod +x /var/www/html/bin/scripts/energenie_switch.py
I had trouble getting this to work because the www-data
needed to be added to the gpio group in order to have access to the GPIO pins of the Raspberry Pi. Having googled around, this is apparently no longer required. However, I still needed to do it:
$ sudo adduser www-data gpio
Redirect traffic
Now, as we mentioned eariler, we need to redirect all the traffic to the single entry point, located in public/index.php
. To do that, we create a .htaccess file in public/
with the following code:
# Redirect to single entry point
RewriteEngine On
# RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
and add another .htaccess file on the project route directory:
RewriteEngine on
RewriteRule ^$ public/ [L]
RewriteRule (.*) public/$1 [L]
This will override any attempt to access a script directly and direct it to the public/index.php
entry point.
And it's ready!
The application should now be available to view and use on our Raspberry IP address, so typing http://192.168.0.31 on a browser should show our fully working application:
Conclusions and furtherwork
With this example, we have shown the whole development, set-up and deployment of a properly structured web application running on a Raspberry Pi plus the use of the PiMote board, as an application to home domotics. Our software and hardfware set-up allows us to switch our sockets on and off as long as we're connected to our house network. Further improvements to the software should allow us to program our sockets at different times of the day as it was the original intention of this article. Due to the length of this article, this will be developed in next article.
Other improvements could involve making the application available outside our network so we can edit our programs and manually switch our Energenie sockets when we are away.