Leon Chu
Published © GPL3+

Solar Battery Powered Switch for Blinds, Lights, and Charger

Control window blinds, lights, and monitor battery charging using voice command.

IntermediateWork in progress12 hours8,700
Solar Battery Powered Switch for Blinds, Lights, and Charger

Things used in this project

Story

Read more

Custom parts and enclosures

Blind Motor Rod Adaptor

Adapter to fit end of blind wand to stepper motor rod.

Schematics

Wire Diagram

Code

Lambda Code on AWS

Python
Interpret Alexa custom skill voice input commands and set settings in IOT Things Shadows.
from __future__ import print_function

import logging
import time
import json
import uuid
import datetime

import boto3

IOT_THING_NAME = "SolarHomeController"
 
client = boto3.client('iot-data')
DEVICE_NAME_LIST= "window blinds, color l.e.d, charger, alarm, light"

def get_utc_timestamp(seconds=None):
    return time.strftime("%Y-%m-%dT%H:%M:%S.00Z", time.gmtime(seconds))

        
def update_shadow(itemKey, value): 
    response = client.update_thing_shadow(
        thingName = IOT_THING_NAME, 
        payload = json.dumps({
            'state': {
                'desired': {
                    itemKey : value
                }
            }})
        )
        
def get_shadow_value(name):
    response = client.get_thing_shadow(thingName = IOT_THING_NAME)
    streamingBody = response["payload"]
    jsonState = json.loads(streamingBody.read())
    if name == "*":
       return jsonState["state"]["reported"]
    return jsonState["state"]["reported"][name]

    

# --------------- Functions that control the skill's behavior ------------------

def get_welcome_response():
    session_attributes = {}
    card_title = "Welcome"
    speech_output = "Welcome to Solar battery power switch. Please specify a command"
    # If the user either does not reply to the welcome message or says something
    # that is not understood, they will be prompted again with this text.
    reprompt_text = "You can say check status. Or turn on a " + \
    "device Here are avaialbe devices names. " + DEVICE_NAME_LIST
  
    should_end_session = False
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))

def handle_session_end_request():
    card_title = "Session Ended"
    speech_output = "Good bye" 
                 
    # Setting this to true ends the session and exits the skill.
    should_end_session = True
    return build_response({}, build_speechlet_response(
        card_title, speech_output, None, should_end_session))


def create_favorite_color_attributes(favorite_color):
    return {"favoriteColor": favorite_color}

def set_color_in_session(intent, session):
    """ Sets the color of LED
    """

    card_title = intent['name']
    session_attributes = {}
    should_end_session = False

    if 'Color' in intent['slots']:
        favorite_color = intent['slots']['Color']['value']
        session_attributes = create_favorite_color_attributes(favorite_color)
        speech_output = "your favoriteis " + \
                        favorite_color + \
                        "what's my favorite color?"
        reprompt_text = "You ca"
    else:
        speech_output = "I'm not sure what your favorite color is. " \
                        "Please try again."
        reprompt_text = "I'm not sure what your favorite color is. " \
                        "You can tell me your favorite color by saying, " \
                        "my favorite color is red."
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))


def get_color_from_session(intent, session):
    session_attributes = {}
    reprompt_text = None

    if session.get('attributes', {}) and "favoriteColor" in session.get('attributes', {}):
        favorite_color = session['attributes']['favoriteColor']
        speech_output = "Your favorite " + favorite_color + \
                        "Right"
        should_end_session = True
    else:
        speech_output = "Please repeat"
        should_end_session = False

    # Setting reprompt_text to None signifies that we do not want to reprompt
    # the user. If the user does not respond or says something that is not
    # understood, the session will end.
    return build_response(session_attributes, build_speechlet_response(
        intent['name'], speech_output, reprompt_text, should_end_session))
       

def get_device_from_session(intent, session):
    session_attributes = {}
    reprompt_text = None

    if session.get('attributes', {}) and "deviceName" in session.get('attributes', {}):
        deviceName = session['attributes']['deviceName']
        speech_output = "You choose Device " + deviceName
        should_end_session = True
    else:
        speech_output = "Please specify a device. Here are list of devices. " + DEVICE_NAME_LIST
        should_end_session = False

    # Setting reprompt_text to None signifies that we do not want to reprompt
    # the user. If the user does not respond or says something that is not
    # understood, the session will end.
    return build_response(session_attributes, build_speechlet_response(
        intent['name'], speech_output, reprompt_text, should_end_session))


        
def check_status(intent):
    card_title = "Status Query"
    keyItems = get_shadow_value("*")
    volt = keyItems["Voltage"]
    condition = "fair"
    if volt > 12.7:
        condition = "full"
    elif volt >= 12.4:
        condition = "good"
    elif volt >= 12.2:
        condition = "fair"
    elif volt >= 11.9:
        condition = "low"
    else:
        condition = "empty"
        
    try:
        speech_output = "Battery is " + condition + " at " + str(volt) + " Volts" 
        speech_output += ". Alarm is " + keyItems["alarm"] 
        speech_output += ". Charger is " + keyItems["charger"] 
        speech_output += ". Light is " + keyItems["light"]
        speech_output += ". l. e. d. Light is " + keyItems["led"]
        speech_output += ". Blinds Position at " + str(keyItems["BlindsPos"]) 
        
    except  KeyError, r:
        speech_output += " Key error"
    should_end_session = True
    return build_response({}, build_speechlet_response(
        card_title, speech_output, None, should_end_session))
    

def set_blinds_position(intent):
    card_title = "Blinds Position"
    should_end_session = True

    speech_output = "" 
    if 'percent' in intent['slots']:  
        if 'value' in intent['slots']['percent']:
            percent = intent['slots']['percent']['value']
            speech_output = "setting position to " + percent
            update_shadow('BlindsPos', int(percent))
    if 'open_close' in intent['slots']:
        if 'value' in intent['slots']['open_close']:

            isOpen = intent['slots']['open_close']['value']
            speech_output = "setting position to " + isOpen
            if isOpen == "open":
                percent = 50
            else:
                percent = 0
            update_shadow('BlindsPos', percent)
        
    if percent is None:
        speech_output = "Position not set."
    return build_response({}, build_speechlet_response(
        card_title, speech_output, None, should_end_session))

def switch_relay(intent, state = "none"):
    card_title = "Switch Device"
    speech_output = "Relay not set" 
    should_end_session = True
    ItemName = "unknown"

    if 'relay_number' in intent['slots']:    
        if 'value' in intent['slots']['relay_number']:
            ItemName = intent['slots']['relay_number']['value']
            speech_output = "setting " + ItemName + " to " + state
    if 'relay_name' in intent['slots']:    
        if 'value' in intent['slots']['relay_name']:
            ItemName = intent['slots']['relay_name']['value']
            speech_output = "setting " + ItemName + " to " + state
        
    if  state in {"On","Off"}:
        if ItemName in {"led","charger","alarm","light"}:
            update_shadow(ItemName, state)
        elif ItemName == "blinds":
            percent = 0
            if state is "On":
                percent = 50
            update_shadow('BlindsPos', percent)
            speech_output = "OK"
        else:
            speech_output = "Please choose one of following device. "  + DEVICE_NAME_LIST
            should_end_session = False
    return build_response({}, build_speechlet_response(
        card_title, speech_output, None, should_end_session))

# --------------- Helpers that build all of the responses ----------------------

def build_speechlet_response(title, output, reprompt_text, should_end_session):
    return {
        'outputSpeech': {
            'type': 'PlainText',
            'text': output
        },
        'card': {
            'type': 'Simple',
            'title': "SessionSpeechlet - " + title,
            'content': "SessionSpeechlet - " + output
        },
        'reprompt': {
            'outputSpeech': {
                'type': 'PlainText',
                'text': reprompt_text
            }
        },
        'shouldEndSession': should_end_session
    }

def build_response(session_attributes, speechlet_response):
    return {
        'version': '1.0',
        'sessionAttributes': session_attributes,
        'response': speechlet_response
    }
# --------------- Events ------------------

def on_session_started(session_started_request, session):
    """ Called when the session starts """

    print("on_session_started requestId=" + session_started_request['requestId']
          + ", sessionId=" + session['sessionId'])
          
def on_launch(launch_request, session):
    print("on_launch requestId=" + launch_request['requestId'] +
          ", sessionId=" + session['sessionId'])
    # Dispatch to your skill's launch
    return get_welcome_response()

def on_intent(intent_request, session=None):
    if session <> None:
        print("on_intent requestId=" + intent_request['requestId'] +
              ", sessionId=" + session['sessionId'])

    intent = intent_request['intent']
    intent_name = intent_request['intent']['name']

    # Dispatch to your skill's intent handlers
  
    if intent_name == "CheckBatteryCapacityIntent":
        return check_status(intent)
    elif intent_name == "SwitchOnRelayIntent":
        return switch_relay(intent,"On")
    elif intent_name == "SwitchOffRelayIntent":
        return switch_relay(intent,"Off")
    elif intent_name == "SetBlindsPositionIntent":
        return set_blinds_position(intent)
    elif intent_name == "AMAZON.HelpIntent":
        return get_welcome_response()
    elif intent_name == "AMAZON.CancelIntent" or intent_name == "AMAZON.StopIntent":
        return handle_session_end_request()
    else:
        raise ValueError("Invalid intent")

def on_session_ended(session_ended_request, session):
    print("on_session_ended requestId=" + session_ended_request['requestId'] +
          ", sessionId=" + session['sessionId'])
    # add cleanup logic here

def lambda_handler(event, context):
    if event['session']['new']:
        on_session_started({'requestId': event['request']['requestId']},
                           event['session'])

    if event['request']['type'] == "LaunchRequest":
        return on_launch(event['request'], event['session'])
    elif event['request']['type'] == "IntentRequest":
        return on_intent(event['request'], event['session'])
    elif event['request']['type'] == "SessionEndedRequest":
        return on_session_ended(event['request'], event['session'])

Alexa Skill Utterance

JSON
{
  "languageModel": {
    "types": [
      {
        "name": "OpenPosition",
        "values": [
          {
            "id": "1",
            "name": {
              "value": "open",
              "synonyms": []
            }
          },
          {
            "id": "0",
            "name": {
              "value": "close",
              "synonyms": [
                "shut"
              ]
            }
          }
        ]
      },
      {
        "name": "RelayName",
        "values": [
          {
            "id": "4",
            "name": {
              "value": "LedLight",
              "synonyms": [
                "led light",
                "led",
                "color",
                "l.e.d."
              ]
            }
          },
          {
            "id": "3",
            "name": {
              "value": "AcLight",
              "synonyms": [
                "ac light",
                "main light",
                "light"
              ]
            }
          },
          {
            "id": "2",
            "name": {
              "value": "Alarm",
              "synonyms": []
            }
          },
          {
            "id": "1",
            "name": {
              "value": "Charger",
              "synonyms": [
                "battery charger"
              ]
            }
          },
          {
            "id": "0",
            "name": {
              "value": "blinds",
              "synonyms": [
                "window",
                "shades",
                "window shades"
              ]
            }
          }
        ]
      }
    ],
    "intents": [
      {
        "name": "AMAZON.CancelIntent",
        "samples": []
      },
      {
        "name": "AMAZON.HelpIntent",
        "samples": []
      },
      {
        "name": "AMAZON.StopIntent",
        "samples": []
      },
      {
        "name": "CheckBatteryCapacityIntent",
        "samples": [
          "check voltage",
          "battery level",
          "remaining capacity"
        ],
        "slots": []
      },
      {
        "name": "SetBlindsPositionIntent",
        "samples": [
          "set Blinds to {percent}",
          "turn blinds {percent}",
          "set blinds position to {percent}",
          "blinds {open_close}"
        ],
        "slots": [
          {
            "name": "percent",
            "type": "AMAZON.NUMBER"
          },
          {
            "name": "open_close",
            "type": "OpenPosition"
          }
        ]
      },
      {
        "name": "SwitchOffRelayIntent",
        "samples": [
          "turn off {relay_name}",
          "switch off {relay_name}",
          "turn {relay_name} off",
          "switch {relay_name} off",
          "turn off switch number {relay_number}",
          "close {relay_name}",
          "shut off {relay_name}",
          "off {relay_name}"
        ],
        "slots": [
          {
            "name": "relay_name",
            "type": "RelayName"
          },
          {
            "name": "relay_number",
            "type": "AMAZON.NUMBER"
          }
        ]
      },
      {
        "name": "SwitchOnRelayIntent",
        "samples": [
          "turn {relay_name} on",
          "switch on {relay_name}",
          "switch {relay_name} on",
          "open {relay_name}",
          "turn on {relay_name}",
          "on {relay_name}"
        ],
        "slots": [
          {
            "name": "relay_number",
            "type": "AMAZON.NUMBER"
          },
          {
            "name": "relay_name",
            "type": "RelayName"
          }
        ]
      }
    ],
    "invocationName": "solar switch"
  }
}

Arduino Yun Sketch

Sketch for battery charging, window blinds control, alarm light trigger, and accessing AWS IoT Things Shadowed.

Credits

Leon Chu

Leon Chu

11 projects • 15 followers
Indie Developer / Artist / Lifelong learner

Comments