Winter is soon to arrive in Sweden and the amount of daylight is decreasing every day. Thus it’s time to set up some extra light sources indoors and outdoors. I have been using my web app for remote controlled outlets (link) for some months now, but with the additional light sources needed for this time of year, I have to extend the application. As Sweden goes into the dark season I would also like to have an on/off schedule for some of the lights so that they are turned on/off automatically according to a set of specified events.
About the original application
For details on the original application, check out my old post. It describes how to sniff/decode the radio signals that need to be recreated from a Raspberry Pi with the help of pi-switch.
Some new receiver outlets
I have now stocked up on some additional receiver sockets for indoor use and also an outdoor outlet from the same brand (Luxorparts, they where on sale) that fits my radio protocol.
Currently I have 10 receivers in total and to address them all I have to use the button number (1-4) and the group code (1-4) in combination. My original remote control application had a fixed group number and could only address 4 receivers, but it can easily be modified so that it handles 4×4=16 receivers.
The new hardware and the updated application will allow me to have remote control of the outdoor Christmas Tree as well as all the compulsory indoor light decorations at this time of year.
So let’s do the software changes…
Modifying the REST API
My app has a REST API backend that runs as a Python Flask service on a Raspberry Pi with Raspbian OS. I will change so that it addresses group number + button number instead of just button number. Using Curl, here are some GET and PUT examples on using the updated API:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Examples on REST calls to the api | |
# Replace localhost:5000 with the IP-address and port of the machine where the api is running | |
# List all defined outlets (GET) | |
curl -i http://localhost:5000/Outlets/api/outlets | |
# Turn on power for outlet 1 in group 2 | |
curl -i -H "Content-Type: application/json" -X PUT -d '{"state":"on"}' http://localhost:5000/Outlets/api/outlets/2/1 | |
# Turn on power for outlet 3 in group 1 | |
curl -i -H "Content-Type: application/json" -X PUT -d '{"state":"on"}' http://localhost:5000/Outlets/api/outlets/1/3 | |
# Turn off power for outlet 3 in group 1 | |
curl -i -H "Content-Type: application/json" -X PUT -d '{"state":"off"}' http://localhost:5000/Outlets/api/outlets/1/3 | |
The main script in the updated Flask application looks like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from flask import Flask, jsonify, request, render_template, abort | |
from outletdefinitions import outlets | |
import codesender | |
app = Flask(__name__) | |
@app.route("/Outlets/api/outlets", methods=["GET"]) | |
def get_outlets(): | |
return jsonify({"outlets" : outlets}) | |
@app.route("/Outlets/",methods=["GET"]) | |
def index(): | |
return render_template("index.html") | |
@app.route("/Outlets/api/outlets/<int:groupNumber>/<int:buttonNumber>",methods=["PUT"]) | |
def clickButton(groupNumber, buttonNumber): | |
state=request.json.get("state") | |
if (state is None): | |
abort(400) | |
if (state.lower() != 'on' and state.lower() != 'off'): | |
abort(400) | |
codesender.sendCode(groupNumber,buttonNumber,state) | |
return state | |
if __name__ == "__main__": | |
app.debug = True | |
app.run(host="0.0.0.0",port=5000) |
It defines end points for getting a list of all defined outlets and for updating the state of an outlet with http PUT. There is also a route for serving up the html page for the Single Page Application (index.html).
The transmission of the radio signals is done in the sendCode method that uses pi-switch:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pi_switch | |
from mylogger import logger | |
byte0codeON = 0x55 | |
byte0codeOFF = 0x54 | |
groupToCodeMap = { | |
1: 0x15, | |
2: 0x45, | |
3: 0x51, | |
4: 0x54 | |
} | |
buttonToCodeMap = { | |
1: 0x15, | |
2: 0x45, | |
3: 0x51, | |
4: 0x54 | |
} | |
def sendCode(groupNumber,buttonNumber,state): | |
if not buttonNumber in buttonToCodeMap.keys(): | |
return | |
if not groupNumber in groupToCodeMap.keys(): | |
return | |
numberCode = buttonToCodeMap[buttonNumber] | |
groupCode = groupToCodeMap[groupNumber] | |
byte0code = byte0codeON | |
if state == 'off': | |
byte0code = byte0codeOFF | |
code = (groupCode << 16) | (numberCode << 8) | byte0code | |
logger.info("Sending code: " + format(code,'000000x')) | |
sender = pi_switch.RCSwitchSender() | |
sender.enableTransmit(0) | |
sender.sendDecimal(code,24) |
Modifying the front end
The application front end is built with AngularJS. It shows a row for each outlet and each row has two columns with an on button in column 1 and an off button in column 2. Instead of binding to just a button number, a binding to both group number and button number will now be used.
The AngularJS-controller handles the button press events and calls the REST API asynchronously with the specified group- and button numbers.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var myApp = angular.module('myApp', []); | |
myApp.controller('OutletController', ['$scope', '$http', function($scope, $http) { | |
$scope.header = 'Outlets'; | |
var getOutletInfo = function() { | |
$http.get("api/outlets").then(function(response) { | |
var outlets = response.data.outlets; | |
$scope.outlets = outlets; | |
}, function(error) {} | |
); | |
}; | |
$scope.pressButton = function(groupNumber,buttonNumber,action) { | |
$http.put("api/outlets/"+groupNumber+"/"+buttonNumber, { state : action}).then(function(response) { | |
}, function(error) {} | |
); | |
} | |
getOutletInfo(); | |
}]); |
Scheduling on/off events
As this time of year in Sweden means more night than day it would be nice to have some lights turned on and off automatically at certain times. To achieve this, I could make a dedicated scheduler, but as my home automation system is based on a Raspberry Pi running Linux, it is more convenient to just set up a cron job that makes Curl calls to my application’s REST backend according to a schedule.
I use “crontab -e” to edit the event table for my user. It will be automatically parsed by cron at regular intervals and if a matching time expression is found, the associated command will be executed. My crontab file looks like this at the moment:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Turn on outdoor lights in the afternoon | |
00 17 * * * curl -i -H "Content-Type: application/json" -X PUT -d '{"state":"on"}' http://localhost:5000/Outlets/api/outlets/2/1 | |
# Turn off outdoor lights in the morning | |
00 08 * * * curl -i -H "Content-Type: application/json" -X PUT -d '{"state":"off"}' http://localhost:5000/Outlets/api/outlets/2/1 | |
# Turn on downstairs window lights in the afternoon | |
30 19 * * * curl -i -H "Content-Type: application/json" -X PUT -d '{"state":"on"}' http://localhost:5000/Outlets/api/outlets/1/2 | |
# Turn on upstairs window lights in the evening | |
30 18 * * * curl -i -H "Content-Type: application/json" -X PUT -d '{"state":"on"}' http://localhost:5000/Outlets/api/outlets/1/1 | |
# Turn off all indoor lights at night | |
# windows upstairs | |
00 22 * * * curl -i -H "Content-Type: application/json" -X PUT -d '{"state":"off"}' http://localhost:5000/Outlets/api/outlets/1/1 | |
# windows downstairs | |
00 22 * * * curl -i -H "Content-Type: application/json" -X PUT -d '{"state":"off"}' http://localhost:5000/Outlets/api/outlets/1/2 | |
# Wall downstairs | |
00 22 * * * curl -i -H "Content-Type: application/json" -X PUT -d '{"state":"off"}' http://localhost:5000/Outlets/api/outlets/1/3 | |
# Floor upstairs | |
00 22 * * * curl -i -H "Content-Type: application/json" -X PUT -d '{"state":"off"}' http://localhost:5000/Outlets/api/outlets/1/4 |
Let there be lights (and lots of them)
The complete code for this application can be fetched from GitHub: https://github.com/LarsBergqvist/RemoteControlled_Outlets
One thought