jeudi 30 janvier 2014

Control your Velux roller shutter with Raspberry Pi and WebIOPi framework

In their basic version, VELUX roller shutters come with a 3 buttons remote controller.
This article proposes a method for controlling your device from a web page or automatizing openings or closures of all your VELUX roller shutters in your home.

Hardware list

Here is the hardware list for this project :

Raspberry Pi board

The Raspberry Pi is a famous credit-card-sized single-board computer than can be purchased for around 30$ (www.raspberrypi.org).
You will also need an Ethernet cable (or WiFi USB dongle) to connect your Raspberry Pi to your LAN.


Velux IO-HomeControl remote controller


This remote controller is required to transmit OPEN / CLOSE commands to IO-HomeControl devices.
You can buy this type of spare remote controller on Ebay market for around 10 $.
Using dedicated procedure, you can easily control several roller-shutters using this single remote controller.


In order to connect this remote controller to your Raspberry Pi and Relay board, you will have to solder some wires:
  • 3 wires (grey) for OPEN / CLOSE / STOP buttons. These wires will be connected to 3 independant relay switches. Each command is triggered by a Low state (Ground) pulse (pulse duration is about 500 ms).
  • 2 Power wires (red and black). These wires will be connected to +3.3v and GND pins of Raspberry Pi GPIO connector.

Relay-Switch board

Relays will be controlled by RaspBerry Pi GPIO and will be connected to the remote controller OPEN/CLOSE/STOP switches. Only three relays are required for this project.


Wiring overview

GPIO mapping :
  • GPIO 17 : OPEN
  • GPIO 18 : CLOSE
  • GPIO 27 : STOP


Software

Raspbian

Your Raspberry needs to run a Linux systems including all drivers for GPIO an network interface (WiFi or Ethernet).
The latest version of Raspbian Linux distribution is definitely a good choice (www.raspbian.org).

WebIOPi framework

The software part of this project is based on WebIOPi framework (https://code.google.com/p/webiopi/).
This framework is written in Python and also includes javascript/HTML library for designing web User Interfaces.
Please refer to WebIOPi website for installation and Python/javascript script / source customization instructions.

Web UI Snapshots

Once WebIOPi service is correctly started on your Raspbery 
This Web UI is accessible on the following :


Velux UP time : Set opening hours
Velux DOWN time : Set closure hour
Automatic UP/DOWN mode : Indicates AUTO mode state
Set Hours : Latch Opening/Closure hours defined in upper fields.
Set mode : Toggles AUTO mode (0=OFF / 1=ON)

Up : Asynchronous OPEN command
Stop : Asynchronous STOP command
Down : Asynchronous DOWN command

Customized source code

Python server script :

download

import webiopi
import datetime

GPIO = webiopi.GPIO

VELUX_UP = 17 # GPIO pin using BCM numbering
VELUX_DOWN = 18 # GPIO pin using BCM numbering
VELUX_STOP = 27 # GPIO pin using BCM numbering

VELUX_STATE = 0 # DOWN
AUTO_MODE = 1

HOUR_UP  = 9   # Turn Velux UP at
HOUR_DOWN = 19 # Turn Velux Down at

# setup function is automatically called at WebIOPi startup
def setup():
  global VELUX_STATE, AUTO_MODE

  # set the GPIO used for VELUX command to output
  GPIO.setFunction(VELUX_UP, GPIO.OUT)
  GPIO.setFunction(VELUX_DOWN, GPIO.OUT)

  # STOP function not used at this time
  GPIO.setFunction(VELUX_STOP, GPIO.OUT)
  GPIO.digitalWrite(VELUX_STOP, GPIO.LOW)


  # retrieve current datetime
  now = datetime.datetime.now()

  if (AUTO_MODE==1):
    # test time
    if ((now.hour >= HOUR_UP) and (now.hour < HOUR_DOWN)):
      GPIO.digitalWrite(VELUX_UP, GPIO.HIGH)
      webiopi.sleep(0.2)
      GPIO.digitalWrite(VELUX_UP, GPIO.LOW)
      VELUX_STATE = 1
    else: 
      GPIO.digitalWrite(VELUX_DOWN, GPIO.HIGH)
      webiopi.sleep(0.2)
      GPIO.digitalWrite(VELUX_DOWN, GPIO.LOW)
      VELUX_STATE = 0

# loop function is repeatedly called by WebIOPi 
def loop():
  global VELUX_STATE, AUTO_MODE

  # retrieve current datetime
  now = datetime.datetime.now()

  if (AUTO_MODE==1):
    # toggle VELUX UP all days at the correct time
    if ((now.hour == HOUR_UP) and (now.minute == 0) and (VELUX_STATE == 0)):
      GPIO.digitalWrite(VELUX_UP, GPIO.HIGH)
      webiopi.sleep(0.2)
      GPIO.digitalWrite(VELUX_UP, GPIO.LOW)
      VELUX_STATE = 1 #UP
        
    # toggle VELUX DOWN all days at the correct time
    if ((now.hour == HOUR_DOWN) and (now.minute == 0) and (VELUX_STATE == 1)):
      GPIO.digitalWrite(VELUX_DOWN, GPIO.HIGH)
      webiopi.sleep(0.2)
      GPIO.digitalWrite(VELUX_DOWN, GPIO.LOW)
      VELUX_STATE = 0 #DOWN
        
  # gives CPU some time before looping again
  webiopi.sleep(1)

# destroy function is called at WebIOPi shutdown
def destroy():
  GPIO.digitalWrite(VELUX_UP, GPIO.LOW)
  GPIO.digitalWrite(VELUX_DOWN, GPIO.LOW)
  GPIO.digitalWrite(VELUX_STOP, GPIO.LOW)


@webiopi.macro
def getHours():
    return "%d;%d" % (HOUR_UP, HOUR_DOWN)

@webiopi.macro
def setHours(on, off):
    global HOUR_UP, HOUR_DOWN
    HOUR_UP = int(on)
    HOUR_DOWN = int(off)
    return getHours()


@webiopi.macro
def getAutoMode():
    return "%d" % (AUTO_MODE)

@webiopi.macro
def setAutoMode():
    global AUTO_MODE
    
    if AUTO_MODE:
      AUTO_MODE=0
    else:
      AUTO_MODE=1
    return getAutoMode()

HTML code for Web UI :

download

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>VELUX Control</title>
        <script type="text/javascript" src="/webiopi.js"></script>
        <script type="text/javascript">
                webiopi().ready(function() {

                // Following function will process data received from set/getHours macro.
                var updateHours = function(macro, args, response) {
                    var hours = response.split(";");
                    // Following lines use jQuery functions
                    $("#inputOn").val(hours[0]);
                    $("#inputOff").val(hours[1]);
                }

                // Following function will process data received from getAutoMode macro.
                var updateAutoMode = function(macro, args, response) {
                    var tmp = response.split(";");
                    // Following lines use jQuery functions
                    $("#AutoMode").val(tmp[0]);
                }


                // Immediately call getHours macro to update the UI with current values
                webiopi().callMacro("getHours", [], updateHours);

                // Immediately call getAutoMode macro to update the UI with current values
                webiopi().callMacro("getAutoMode", [], updateAutoMode);



                // Create a button to call setLightHours macro
                var sendButton = webiopi().createButton("sendButton", "Set Hours", function() {
                   // Arguments sent to the macro
                    var hours = [$("#inputOn").val(), $("#inputOff").val()];
                    // Call the macro

                    webiopi().callMacro("setHours", hours, updateHours);



                // Append the button to the controls box using a jQuery function
                $("#controls").append(sendButton);


                // Create a button to call setAutoMode macro
                var automodeButton = webiopi().createButton("automodeButton", "Set Mode", function() {
                    // Call the macro
                    webiopi().callMacro("setAutoMode",[], updateAutoMode);
                });

                // Append the button to the controls box using a jQuery function
                $("#controls").append(automodeButton);

                // create a "UP" button that output a single pulse on GPIO 17
                //var button_up = webiopi().createPulseButton("up", "Up", 17);
                var button_up = webiopi().createSequenceButton("up","Up",17,100,"011110");
                $("#controls").append(button_up); // append button to content div

                // create a "STOP" button that output a single pulse on GPIO 27
                var button_stop = webiopi().createSequenceButton("stop","Stop",27,200,"011110");
                $("#controls").append(button_stop); // append button to content div

                // create a "DOWN" button that output a single pulse on GPIO 18
                //var button_down = webiopi().createPulseButton("down", "Down", 18);
                var button_down = webiopi().createSequenceButton("down","Down",18,100,"011110");
                $("#controls").append(button_down); // append button to content div

                // Refresh GPIO buttons
                // pass true to refresh repeatedly of false to refresh once
                webiopi().refreshGPIO(true);

        });

        </script>
        <style type="text/css">
                button {
                        display: block;
                        margin: 5px 5px 5px 5px;
                        width: 160px;
                        height: 45px;
                        font-size: 24pt;
                        font-weight: bold;
                        color: white;
                }
        </style>
</head>

<body>
<div align="center">
Velux UP time (hour):<input type="text" id="inputOn" /><br/>
Velux DOWN time (hour): <input type="text" id="inputOff" /><br/>
Automatic UP/DOWN mode: <input type="text" id="AutoMode" /><br/>
<HR>

<div id="controls"></div>
</div>
</body>

</html>