Darian Johnson
Published © GPL3+

DIY Smart Lamp - Controlled by Toggle Switch and Alexa

Build a smart lamp that can be controlled by flipping a switch and Alexa, for only $35.

IntermediateFull instructions provided20 hours11,396
DIY Smart Lamp - Controlled by Toggle Switch and Alexa

Things used in this project

Hardware components

Relay (generic)
×1
Toggle Switch
You could also use a push button
×1
Floor Lamp
×1
Plastic Project Box
×1
Prototype PCB Board
×1
Jumper wires (generic)
Jumper wires (generic)
×1
22 awg wire
×1

Software apps and online services

Alexa Skills Kit
Amazon Alexa Alexa Skills Kit
AWS Lambda
Amazon Web Services AWS Lambda
AWS IoT
Amazon Web Services AWS IoT

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Schematic for smart lamp

Code

Button.py

Python
Python Code to control button
import mraa  
import time
    
# Refer to the pin-out diagram for the GPIO number
#button pnb
buttonpin = mraa.Gpio(43)  
buttonpin.dir(mraa.DIR_IN)
current_button = buttonpin.read()    

relaypin = mraa.Gpio(3)
relaypin.dir(mraa.DIR_IN)

relaypin_out = mraa.Gpio(3)
relaypin_out.dir(mraa.DIR_OUT)

while True:
	if buttonpin.read() != current_button: #button was flipped
		time.sleep(0.25)
		print "button flipped"
		print "relay:", relaypin.read()
		if relaypin.read() == 0:
			relaypin_out.write(1)
		else:
			relaypin_out.write(0)
		current_button = buttonpin.read()

LAMPBUTTON

SH
Script that defines service for button.py
#!/bin/sh /etc/rc.common
START=99
STOP=15

boot() {
    # launch the python script
    python /root/button.py &
}

start() {        
   python /root/button.py &
   echo start
   # commands to launch application
}                 
 
stop() {          
   pid=`ps -ef | grep '[p]ython /root/button.py' | awk '{ print $2 }'`
   echo $pid
   kill $pid
   sleep 2
   echo "Server killed."
   # commands to kill application 
}

restart() {        
    # launch the python script
    python /root/button.py &
}

LAMPMQTT

SH
script that defines service for mqtt.py
#!/bin/sh /etc/rc.common
START=99
STOP=15

boot() {
    # launch the python script
    python /root/mqtt.py &
}

start() {        
   python /root/lamp/mqtt.py &
   echo start
   # commands to launch application
}                 
 
stop() {          
   pid=`ps -ef | grep '[p]ython /root/mqtt.py' | awk '{ print $2 }'`
   echo $pid
   kill $pid
   sleep 2
   echo "Server killed."
   # commands to kill application 
}

restart() {        
    # launch the python script
    python /root/mqtt.py &
}

MQTT.py

Python
Code to receive MQTT messages
import mraa  
import time
import paho.mqtt.client as paho
import ssl
import json

# relay pin out
relaypin_out =  mraa.Gpio(3)
relaypin_out.dir(mraa.DIR_OUT)   


# Parameters
topic = "user@emal.com"
applianceId = "DEVICE-NAME"


def on_connect(client, userdata, flags, rc):
	print("Connection returned result: " + str(rc) )
	# Subscribing in on_connect() means that if we lose the connection and
	# reconnect then subscriptions will be renewed.
	#client.subscribe("#" , 1 )
	client.subscribe(topic + "/" + applianceId + "/#" ,1)

def on_message(client, userdata, msg):
	print("payload: " + msg.payload)
	parsed_json = json.loads(msg.payload)
	
	if msg.topic ==topic + "/" + applianceId + "/on":
		relaypin_out.write(1)
	
	if msg.topic ==topic + "/" + applianceId + "/off":
		relaypin_out.write(0)
	

mqttc = paho.Client()
mqttc.on_connect = on_connect
mqttc.on_message = on_message


#variables to connect to AWS IoT
#Note these certs allow access to send IoT messages
awshost = "data.iot.us-east-1.amazonaws.com"
awsport = 8883
clientId = applianceId
thingName = applianceId
caPath = "certs/verisign-cert.pem"
certPath = "certs/Light.certificate.pem.crt"
keyPath = "certs/Light.private.pem.key"

mqttc.tls_set(caPath, certfile=certPath, keyfile=keyPath, cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLSv1_2, ciphers=None)

mqttc.connect(awshost, awsport, keepalive=60)

mqttc.loop_forever() 

index.sh

JavaScript
AWS Lambda code that defines the Alexa Skill
var AWS = require('aws-sdk'); 
var iotdata = new AWS.IotData({endpoint: 'XXXXXXXX.iot.us-east-1.amazonaws.com'});
var request = require('request');

// namespaces
const NAMESPACE_CONTROL = "Alexa.ConnectedHome.Control";
const NAMESPACE_DISCOVERY = "Alexa.ConnectedHome.Discovery";

// discovery
const REQUEST_DISCOVER = "DiscoverAppliancesRequest";
const RESPONSE_DISCOVER = "DiscoverAppliancesResponse";

// control
const REQUEST_TURN_ON = "TurnOnRequest";
const RESPONSE_TURN_ON = "TurnOnConfirmation";
const REQUEST_TURN_OFF = "TurnOffRequest";
const RESPONSE_TURN_OFF = "TurnOffConfirmation";

// errors
const ERROR_UNSUPPORTED_OPERATION = "UnsupportedOperationError";
const ERROR_UNEXPECTED_INFO = "UnexpectedInformationReceivedError";


// entry
exports.handler = function (event, context, callback) {

  log("Received Directive", event);
  var requestedNamespace = event.header.namespace;
  var response = null;

  try {

    switch (requestedNamespace) {

      case NAMESPACE_DISCOVERY:
        response = handleDiscovery(event);
        break;

      case NAMESPACE_CONTROL:
        response = handleControl(event);
        break;

      default:
        log("Error", "Unsupported namespace: " + requestedNamespace);
        response = handleUnexpectedInfo(requestedNamespace);
        break;

    }// switch

  } catch (error) {

    log("Error", error);

  }// try-catch

  callback(null, response);

};// exports.handler


var handleDiscovery_old = function(event) {

  var header = createHeader(NAMESPACE_DISCOVERY, RESPONSE_DISCOVER);
  var payload = {
    "discoveredAppliances": []
  };

  return createDirective(header,payload);

};// handleDiscovery

var handleDiscovery = function(event, context) {
  var header = createHeader(NAMESPACE_DISCOVERY,RESPONSE_DISCOVER);
  // initialize empty payload
  var payload = {};
  if (event.header.name == REQUEST_DISCOVER) {
   // function to retrieve device info from device cloud
   var appliances = getAppliances(event);
   payload = {
      "discoveredAppliances": appliances
   };
  }
  return createDirective(header,payload);
};// handleDiscovery


var handleControl = function(event) {

  var response = null;
  var requestedName = event.header.name;

  switch (requestedName) {

    case REQUEST_TURN_ON :
      response = handleControlTurnOn(event);
      break;

    case REQUEST_TURN_OFF :
      response = handleControlTurnOff(event);
      break;

    default:
      log("Error", "Unsupported operation" + requestedName);
      response = handleUnsupportedOperation();
      break;     

  }// switch

  return response;

};// handleControl


var handleControlTurnOn = function(event) {
  var header = createHeader(NAMESPACE_CONTROL,RESPONSE_TURN_ON);
  var payload = {};
  callDeviceCloud(event,"state","on");
  return createDirective(header,payload);
};// handleControlTurnOn


var handleControlTurnOff = function(event) {
  var header = createHeader(NAMESPACE_CONTROL,RESPONSE_TURN_OFF);
  callDeviceCloud(event,"state","off");
  var payload = {};
  return createDirective(header,payload);
};// handleControlTurnOff


var handleUnsupportedOperation = function() {

  var header = createHeader(NAMESPACE_CONTROL,ERROR_UNSUPPORTED_OPERATION);
  var payload = {};
  return createDirective(header,payload);

};// handleUnsupportedOperation


var handleUnexpectedInfo = function(fault) {

  var header = createHeader(NAMESPACE_CONTROL,ERROR_UNEXPECTED_INFO);
  var payload = {
    "faultingParameter" : fault
  };

  return createDirective(header,payload);

};// handleUnexpectedInfo


// ------------------------------support functions---------------------------------------------

var createMessageId = function() {

  var d = new Date().getTime();
  var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {

    var r = (d + Math.random()*16)%16 | 0;
    d = Math.floor(d/16);
    return (c=='x' ? r : (r&0x3|0x8)).toString(16);

  });
  return uuid;

};// createMessageId


var createHeader = function(namespace, name) {

  return {
    "messageId": createMessageId(),
    "namespace": namespace,
    "name": name,
    "payloadVersion": "2"
  };
};// createHeader


var createDirective = function(header, payload) {

  return {
    "header" : header,
    "payload" : payload
  };

};// createDirective


var log = function(title, msg) {
  console.log('**** ' + title + ': ' + JSON.stringify(msg));
};// log




//--------------messages to device---------------------//

var getAppliances = function(event) {
  // var accessToken = event.payload.accessToken
  return [
    {
        "applianceId": "DEVICE-NAME",
        "manufacturerName": "Your Name",
        "modelName": "DIY Lamp",
        "version": "1",
        "friendlyName": "ROOM",
        "friendlyDescription": "DIY Smart Light",
        "isReachable": true,
        "actions": [
            "turnOn",
            "turnOff"
        ],
        "additionalApplianceDetails": {
            "extraDetail1": "This is a light that is reachable"
        }
    }    
  ];
};// getSampleAppliances

var callDeviceCloud = function(event, command, commandValue) {
  var deviceId = event.payload.appliance.applianceId;
  log(deviceId, command + " = " + commandValue);
  var email = 'user@email.com';
  var iotTopic = email + "/" + deviceId + "/" + commandValue;
  var iotPayload = '{ "message": "Toggle Light"}';
  
  publishMessage(iotTopic,iotPayload);
};// callDeviceCloud

function publishMessage(iotTopic, iotPayload){
    
	var iotParams = {
		topic: iotTopic, // required
		//payload: new Buffer('...') || payload,
		payload: iotPayload,
		qos: 1
	};
	
	iotdata.publish(iotParams, function(err, data) {
		if (err) console.log(err, err.stack); // an error occurred
		else     console.log(data);  
		
	});

}


function getProfileInfo(){
    var accessToken = event.payload.accessToken;
    var amznProfileURL = 'https://api.amazon.com/user/profile?access_token=';

    request(amznProfileURL, function(error, response, body) {
        if (response.statusCode == 200) {
                var profile = JSON.parse(body);
                console.log(profile.CustomerId);
                return profile.CustomerId;
            } else {
                console.log("Customer not found");
                return null;
            }

        });

}

Credits

Darian Johnson

Darian Johnson

8 projects • 140 followers
Technologist. Music lover. Fitness enthusiast. Movie buff. Fan of sci-fi and comic books.

Comments