diff --git a/modules/distribution/src/sketches/firealarm/RaspberryStats.py b/modules/distribution/src/sketches/firealarm/RaspberryStats.py new file mode 100644 index 00000000..cd05ce4c --- /dev/null +++ b/modules/distribution/src/sketches/firealarm/RaspberryStats.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python + +""" +/** +* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +* +* WSO2 Inc. licenses this file to you under the Apache License, +* Version 2.0 (the "License"); you may not use this file except +* in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +**/ +""" + + +import logging, logging.handlers +import sys, os, signal, argparse +import httplib, time +import threading +import Adafruit_DHT # Adafruit library required for temperature sensing + +import iotUtils +import httpServer # python script used to start a http-server to listen for operations (includes the TEMPERATURE global variable) +import xmppServer # python script used to communicate with xmpp server +import mqttListener # python script used to accept messages via mqtt + + + +PUSH_INTERVAL = 300 # time interval between successive data pushes in seconds +logging_enabled = True + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Endpoint specific settings to which the data is pushed +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +DC_IP = '204.232.188.214' #'192.168.57.128' +DC_PORT = 8281 +HOST = DC_IP + ':' + `DC_PORT` + +DC_ENDPOINT = '/firealarm/1.0/controller' #'/firealarm/1.0/' +PUSH_ENDPOINT = DC_ENDPOINT + '/push_temperature' +REGISTER_ENDPOINT = DC_ENDPOINT + '/register' +### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Logger defaults +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LOG_FILENAME = "/usr/local/src/RaspberryAgent/logs/RaspberryStats.log" +LOG_LEVEL = logging.INFO # Could be e.g. "DEBUG" or "WARNING" +### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Define and parse command line arguments +# If the log file is specified on the command line then override the default +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +parser = argparse.ArgumentParser(description="Python service to push RPi info to the Device Cloud") +parser.add_argument("-l", "--log", help="file to write log to (default '" + LOG_FILENAME + "')") + +help_string = "time interval between successive data pushes (default '" + str(PUSH_INTERVAL) + "')" +parser.add_argument("-i", "--interval", type=int, help=help_string) + +args = parser.parse_args() +if args.log: + LOG_FILENAME = args.log + +if args.interval: + PUSH_INTERVAL = args.interval +### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# A class we can use to capture stdout and sterr in the log +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class IOTLogger(object): + def __init__(self, logger, level): + """Needs a logger and a logger level.""" + self.logger = logger + self.level = level + + def write(self, message): + if message.rstrip() != "": # Only log if there is a message (not just a new line) + self.logger.log(self.level, message.rstrip()) +### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Configure logging to log to a file, +# making a new file at midnight and keeping the last 3 day's data +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +def configureLogger(loggerName): + logger = logging.getLogger(loggerName) + logger.setLevel(LOG_LEVEL) # Set the log level to LOG_LEVEL + handler = logging.handlers.TimedRotatingFileHandler(LOG_FILENAME, when="midnight", backupCount=3) # Handler that writes to a file, + # ~~~make new file at midnight and keep 3 backups + formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s') # Format each log message like this + handler.setFormatter(formatter) # Attach the formatter to the handler + logger.addHandler(handler) # Attach the handler to the logger + + if(logging_enabled): + sys.stdout = IOTLogger(logger, logging.INFO) # Replace stdout with logging to file at INFO level + sys.stderr = IOTLogger(logger, logging.ERROR) # Replace stderr with logging to file at ERROR level +### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# This method registers the DevieIP in the Device-Cloud +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +def registerDeviceIP(): + dcConncection = httplib.HTTPConnection(DC_IP, DC_PORT) + dcConncection.set_debuglevel(1) + dcConncection.connect() + + registerURL = REGISTER_ENDPOINT + '/' + iotUtils.DEVICE_OWNER + '/' + iotUtils.DEVICE_ID + '/' + iotUtils.HOST_NAME + + dcConncection.putrequest('POST', registerURL) + dcConncection.putheader('Authorization', 'Bearer ' + iotUtils.AUTH_TOKEN) + dcConncection.endheaders() + + dcConncection.send('') + dcResponse = dcConncection.getresponse() + + print '~~~~~~~~~~~~~~~~~~~~~~~~ Device Registration ~~~~~~~~~~~~~~~~~~~~~~~~~' + print registerURL + print dcResponse.status, dcResponse.reason + print dcResponse.msg + + dcConncection.close() + print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' +### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# This method connects to the Device-Cloud and pushes data +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +def connectAndPushData(): + dcConncection = httplib.HTTPConnection(DC_IP, DC_PORT) + dcConncection.set_debuglevel(1) + + dcConncection.connect() + + request = dcConncection.putrequest('POST', PUSH_ENDPOINT) + + headers = {} + headers['Authorization'] = 'Bearer ' + iotUtils.AUTH_TOKEN + headers['Content-Type'] = 'application/json' + + ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ### Read the Temperature and Load info of RPi and construct payload + ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + # rPiTemperature=getCPUTemp() # Can be used if required to push CPU Temperature + # rPiLoad = getCPULoad() # Can be used if required to push CPU Load + + ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + rPiTemperature = iotUtils.LAST_TEMP # Push the last read temperature value + PUSH_DATA = iotUtils.DEVICE_INFO + iotUtils.DEVICE_IP.format(ip=iotUtils.HOST_NAME) + iotUtils.DEVICE_DATA.format(temperature=rPiTemperature) + PUSH_DATA += '}' + + print PUSH_DATA + + headers['Content-Length'] = len(PUSH_DATA) + + for k in headers: + dcConncection.putheader(k, headers[k]) + dcConncection.endheaders() + + dcConncection.send(PUSH_DATA) # Push the data + dcResponse = dcConncection.getresponse() + + print dcResponse.status, dcResponse.reason + print dcResponse.msg + + dcConncection.close() + print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' + + if (dcResponse.status == 409 or dcResponse.status == 412): + print 'Re-registering Device IP' + registerDeviceIP() + +### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# This is a Thread object for reading temperature continuously +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class TemperatureReaderThread(object): + def __init__(self, interval=3): + self.interval = interval + + thread = threading.Thread(target=self.run, args=()) + thread.daemon = True # Daemonize thread + thread.start() # Start the execution + + def run(self): + TEMP_PIN = 4 + TEMP_SENSOR_TYPE = 11 + + # Try to grab a sensor reading. Use the read_retry method which will retry up + # to 15 times to get a sensor reading (waiting 2 seconds between each retry). + while True: + try: + humidity, temperature = Adafruit_DHT.read_retry(TEMP_SENSOR_TYPE, TEMP_PIN) + + if temperature != iotUtils.LAST_TEMP: + iotUtils.LAST_TEMP = temperature + connectAndPushData() + + iotUtils.LAST_TEMP = temperature + print 'Temp={0:0.1f}*C Humidity={1:0.1f}%'.format(temperature, humidity) + + except Exception, e: + print "Exception in TempReaderThread: Could not successfully read Temperature" + print str(e) + print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' + pass + + time.sleep(self.interval) +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# This is a Thread object for listening for MQTT Messages +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class UtilsThread(object): + def __init__(self): + thread = threading.Thread(target=self.run, args=()) + thread.daemon = True # Daemonize thread + thread.start() # Start the execution + + def run(self): + iotUtils.main() +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# This is a Thread object for HTTP-Server that listens for operations on RPi +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class ListenHTTPServerThread(object): + def __init__(self): + thread = threading.Thread(target=self.run, args=()) + thread.daemon = True # Daemonize thread + thread.start() # Start the execution + + def run(self): + httpServer.main() +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# This is a Thread object for Server that listens for XMPP Messages +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class ListenXMPPServerThread(object): + def __init__(self): + thread = threading.Thread(target=self.run, args=()) + thread.daemon = True # Daemonize thread + thread.start() # Start the execution + + def run(self): + xmppServer.main() +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# This is a Thread object for listening for MQTT Messages +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class ListenMQTTThread(object): + def __init__(self): + thread = threading.Thread(target=self.run, args=()) + thread.daemon = True # Daemonize thread + thread.start() # Start the execution + + def run(self): + mqttListener.main() +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# The Main method of the RPi Agent +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +def main(): + configureLogger("WSO2IOT_RPiStats") +# iotUtils.setUpGPIOPins() + + UtilsThread() + registerDeviceIP() # Call the register endpoint and register Device IP + TemperatureReaderThread() # initiates and runs the thread to continuously read temperature from DHT Sensor + ListenHTTPServerThread() # starts an HTTP Server that listens for operational commands to switch ON/OFF Led + ListenXMPPServerThread() + ListenMQTTThread() + + while True: + try: + if iotUtils.LAST_TEMP > 0: # Push data only if there had been a successful temperature read + connectAndPushData() # Push Sensor (Temperature) data to WSO2 BAM + time.sleep(PUSH_INTERVAL) + except (KeyboardInterrupt, Exception) as e: + print "Exception in RaspberryAgentThread (either KeyboardInterrupt or Other):" + print str(e) + print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' + pass +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +if __name__ == "__main__": + main() diff --git a/modules/distribution/src/sketches/firealarm/httpServer.py b/modules/distribution/src/sketches/firealarm/httpServer.py new file mode 100644 index 00000000..631aeaf2 --- /dev/null +++ b/modules/distribution/src/sketches/firealarm/httpServer.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python + +""" +/** +* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +* +* WSO2 Inc. licenses this file to you under the Apache License, +* Version 2.0 (the "License"); you may not use this file except +* in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +**/ +""" + +import time +import BaseHTTPServer +import iotUtils + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# HOST and PORT info of the HTTP Server that gets started +# HOST_NAME is initialised in the main() method +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#global HOST_NAME +#HOST_NAME = "0.0.0.0" + +SERVER_PORT = 80 # Maybe set this to 9000. +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Class that handles HTTP GET requests for operations on the RPi +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler): + def do_GET(request): + # """Respond to a GET request.""" + + if not processURLPath(request.path): + return + + resource = request.path.split("/")[1].upper() + state = request.path.split("/")[2].upper() + print "Resource: " + resource + + if resource == "TEMP": + request.send_response(200) + request.send_header("Content-type", "text/plain") + request.end_headers() + request.wfile.write(iotUtils.LAST_TEMP) + + elif resource == "BULB": + iotUtils.switchBulb(state) + print "Requested Switch State: " + state + + elif resource == "SONAR": + request.send_response(200) + request.send_header("Content-type", "text/plain") + request.end_headers() + request.wfile.write(iotUtils.LAST_DISTANCE) + + print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Check the URL string of the request and validate +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +def processURLPath(path): + if path.count("/") != 2 and not "favicon" in path: + print "Invalid URL String: " + path + return False + + resource = path.split("/")[1] + + if not iequal("BULB", resource) and not iequal("TEMP", resource) and not iequal("FAN", resource) and not iequal("SONAR", resource): + if not "favicon" in resource: + print "Invalid resource: " + resource + " to execute operation" + return False + + return True +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Case-Insensitive check on whether two string are similar +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +def iequal(a, b): + try: + return a.upper() == b.upper() + except AttributeError: + return a == b +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# The Main method of the server script +# This method is invoked from RaspberryStats.py on a new thread +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +def main(): + HOST_NAME = iotUtils.getDeviceIP() + server_class = BaseHTTPServer.HTTPServer + + while True: + try: + httpd = server_class((HOST_NAME, SERVER_PORT), MyHandler) + print time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, SERVER_PORT) + + httpd.serve_forever() + except (KeyboardInterrupt, Exception) as e: + print "Exception in ServerThread (either KeyboardInterrupt or Other):" + print str(e) + +# GPIO.output(BULB_PIN, False) + iotUtils.switchBulb("OFF") + httpd.server_close() + print time.asctime(), "Server Stops - %s:%s" % (HOST_NAME, SERVER_PORT) + print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' + pass + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +if __name__ == '__main__': + main() + diff --git a/modules/distribution/src/sketches/firealarm/iotUtils.py b/modules/distribution/src/sketches/firealarm/iotUtils.py new file mode 100644 index 00000000..92903710 --- /dev/null +++ b/modules/distribution/src/sketches/firealarm/iotUtils.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python + +""" +/** +* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +* +* WSO2 Inc. licenses this file to you under the Apache License, +* Version 2.0 (the "License"); you may not use this file except +* in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +**/ +""" + +import time, commands +# import threading +import RPi.GPIO as GPIO +import ConfigParser + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# HOST_NAME(IP) of the Device +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +global HOST_NAME +HOST_NAME = "0.0.0.0" +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +global LAST_TEMP +LAST_TEMP = 25 # The Last read temperature value from the DHT sensor. Kept globally + # Updated by the temperature reading thread + +global LAST_DISTANCE +LAST_DISTANCE = 100 + +SONAR_TRIG_PIN = 16 #Associate pin 23 to TRIG +SONAR_ECHO_PIN = 18 +BULB_PIN = 11 # The GPIO Pin# in RPi to which the LED is connected + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Device specific info when pushing data to server +# Read from a file "deviceConfigs.cfg" in the same folder level +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +configParser = ConfigParser.RawConfigParser() +configFilePath = r'./deviceConfigs.cfg' +configParser.read(configFilePath) + +DEVICE_OWNER = configParser.get('Device-Configurations', 'owner') +DEVICE_ID = configParser.get('Device-Configurations', 'deviceId') +MQTT_EP = configParser.get('Device-Configurations', 'mqtt-ep') +XMPP_EP = configParser.get('Device-Configurations', 'xmpp-ep') +AUTH_TOKEN = configParser.get('Device-Configurations', 'auth-token') + +DEVICE_INFO = '{"owner":"'+ DEVICE_OWNER + '","deviceId":"' + DEVICE_ID + '","reply":' +DEVICE_IP = '"{ip}","value":' +DEVICE_DATA = '"{temperature}"' # '"{temperature}:{load}:OFF"' +### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Method used to switch ON/OFF the LED attached to RPi +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +def switchBulb(state): + print "Requested Switch State: " + state + + if state == "ON": + GPIO.output(BULB_PIN, True) + print "BULB Switched ON" + elif state == "OFF": + GPIO.output(BULB_PIN, False) + print "BULB Switched OFF" + + print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Get the wlan0 interface via which the RPi is connected +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +def getDeviceIP(): + rPi_IP = commands.getoutput("ip route list | grep 'src '").split() + rPi_IP = rPi_IP[rPi_IP.index('src') + 1] + + if len(rPi_IP)<=16: + print "------------------------------------------------------------------------------------" + print "IP Address of RaspberryPi: " + rPi_IP + print "------------------------------------------------------------------------------------" + return rPi_IP +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Set the GPIO pin modes for the ones to be read +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +def setUpGPIOPins(): + try: + GPIO.setwarnings(False) + GPIO.setmode(GPIO.BOARD) + except Exception as e: + print "Exception at 'GPIO.setmode'" + pass + + GPIO.setup(SONAR_TRIG_PIN,GPIO.OUT) #Set pin as GPIO out + GPIO.setup(SONAR_ECHO_PIN,GPIO.IN) #Set pin as GPIO in + GPIO.setup(BULB_PIN, GPIO.OUT) + GPIO.output(BULB_PIN, False) +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# This method get the CPU Temperature of the Raspberry Pi +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +def getCPUTemp(): + CPU_TEMP_LOC = "/sys/class/thermal/thermal_zone0/temp" # RaspberryPi file location to get CPU TEMP info + tempFile = open(CPU_TEMP_LOC) + cpuTemp = tempFile.read() + cpuTemp = long(float(cpuTemp)) + cpuTemp = cpuTemp * 1.0 / 1000.0 + print "The CPU temperature is: %.2f" % cpuTemp + return cpuTemp +### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# This method get the CPU Load of the Raspberry Pi +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +def getCPULoad(): + CPU_LOAD_LOC = "/proc/loadavg" # RaspberryPi file location to get CPU LOAD info + loadFile = open(CPU_LOAD_LOC) + cpuLoad = loadFile.read() + cpuLoad = cpuLoad.split()[0] + cpuLoad = long(float(cpuLoad)) + print "The CPU temperature is: %.2f" % cpuLoad + return cpuLoad +### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# This is a Thread object for reading sonar values continuously +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# class SonarReaderThread(object): +# def __init__(self, interval=3): +# self.interval = interval +# thread = threading.Thread(target=self.run, args=()) +# thread.daemon = True # Daemonize thread +# thread.start() # Start the execution + +# def run(self): +# while True: +# try: +# GPIO.output(SONAR_TRIG_PIN, False) #Set TRIG as LOW +# print "SONAR: Waitng For Sonar Sensor To Settle" +# time.sleep(0.5) #Delay of 2 seconds + +# GPIO.output(SONAR_TRIG_PIN, True) #Set TRIG as HIGH +# time.sleep(0.00001) #Delay of 0.00001 seconds +# GPIO.output(SONAR_TRIG_PIN, False) #Set TRIG as LOW + +# while GPIO.input(SONAR_ECHO_PIN)==0: #Check whether the ECHO is LOW +# pulse_start = time.time() #Saves the last known time of LOW pulse + +# while GPIO.input(SONAR_ECHO_PIN)==1: #Check whether the ECHO is HIGH +# pulse_end = time.time() #Saves the last known time of HIGH pulse + +# pulse_duration = pulse_end - pulse_start #Get pulse duration to a variable + +# distance = pulse_duration * 17150 #Multiply pulse duration by 17150 to get distance +# distance = round(distance, 2) #Round to two decimal points + +# if distance > 2 and distance < 400: #Check whether the distance is within range +# print "SONAR: Distance: ", distance - 0.5,"cm" #Print distance with 0.5 cm calibration +# else: +# print "SONAR: Out Of Range" #display out of range + +# except Exception, e: +# print "SONAR: Exception in SonarReaderThread: Could not successfully read Sonar" +# print str(e) +# print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' +# pass +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def readSonarDistance(): + global LAST_DISTANCE + try: + GPIO.output(SONAR_TRIG_PIN, False) #Set TRIG as LOW + print "SONAR: Waitng For Sonar Sensor To Settle" + time.sleep(0.5) #Delay of 2 seconds + + GPIO.output(SONAR_TRIG_PIN, True) #Set TRIG as HIGH + time.sleep(0.00001) #Delay of 0.00001 seconds + GPIO.output(SONAR_TRIG_PIN, False) #Set TRIG as LOW + + while GPIO.input(SONAR_ECHO_PIN)==0: #Check whether the ECHO is LOW + pulse_start = time.time() #Saves the last known time of LOW pulse + + while GPIO.input(SONAR_ECHO_PIN)==1: #Check whether the ECHO is HIGH + pulse_end = time.time() #Saves the last known time of HIGH pulse + + pulse_duration = pulse_end - pulse_start #Get pulse duration to a variable + + distance = pulse_duration * 17150 #Multiply pulse duration by 17150 to get distance + distance = round(distance, 2) #Round to two decimal points + + if distance > 2 and distance < 400: #Check whether the distance is within range + print "SONAR: Distance: ", distance - 0.5,"cm" #Print distance with 0.5 cm calibration + LAST_DISTANCE = distance + else: + print "SONAR: Out Of Range" #display out of range + + except Exception, e: + print "SONAR: Exception in SonarReaderThread: Could not successfully read Sonar" + print str(e) + print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# The Main method of the server script +# This method is invoked from RaspberryStats.py on a new thread +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +def main(): + HOST_NAME = getDeviceIP() + setUpGPIOPins() + # SonarReaderThread() + while True: + readSonarDistance() + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +if __name__ == '__main__': + main() + diff --git a/modules/distribution/src/sketches/firealarm/mqttListener.py b/modules/distribution/src/sketches/firealarm/mqttListener.py new file mode 100644 index 00000000..6daca497 --- /dev/null +++ b/modules/distribution/src/sketches/firealarm/mqttListener.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python + +""" +/** +* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +* +* WSO2 Inc. licenses this file to you under the Apache License, +* Version 2.0 (the "License"); you may not use this file except +* in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +**/ +""" + +import time +import iotUtils +#import RPi.GPIO as GPIO +import paho.mqtt.client as mqtt + + +# The callback for when the client receives a CONNACK response from the server. +def on_connect(client, userdata, flags, rc): + print("Connected with result code "+str(rc)) + + # Subscribing in on_connect() means that if we lose the connection and + # reconnect then subscriptions will be renewed. + print ("Subscribing with topic " + TOPIC) + client.subscribe(TOPIC) + + + +# The callback for when a PUBLISH message is received from the server. +def on_message(client, userdata, msg): + print(msg.topic+" "+str(msg.payload)) + + request = str(msg.payload) + + resource = request.split(":")[0].upper() + state = request.split(":")[1].upper() + + print "Resource: " + resource + + if resource == "TEMP": + pass + #request.send_response(200) + #request.send_header("Content-type", "text/plain") + #request.end_headers() + #request.wfile.write(LAST_TEMP) + # return + + elif resource == "BULB": + iotUtils.switchBulb(state) +# print "Requested Switch State: " + state +# if state == "ON": +# GPIO.output(BULB_PIN, True) +# print "BULB Switched ON" +# elif state == "OFF": +# GPIO.output(BULB_PIN, False) +# print "BULB Switched OFF" + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# The Main method of the server script +# This method is invoked from RaspberryStats.py on a new thread +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +def main(): + + MQTT_ENDPOINT = iotUtils.MQTT_EP.split(":") + MQTT_IP = MQTT_ENDPOINT[0] + MQTT_PORT = MQTT_ENDPOINT[1] + + DEV_OWNER = iotUtils.DEVICE_OWNER + DEV_ID = iotUtils.DEVICE_ID + + global TOPIC + TOPIC = "wso2/iot/" + DEV_OWNER + "/firealarm/" + DEV_ID + + print ("MQTT_ENDPOINT: " + str(MQTT_ENDPOINT)) + print ("MQTT_TOPIC: " + TOPIC) + + mqttClient = mqtt.Client() + mqttClient.on_connect = on_connect + mqttClient.on_message = on_message + + while True: + try: + mqttClient.connect(MQTT_IP, MQTT_PORT, 60) + print time.asctime(), "Connected to MQTT Broker - %s:%s" % (MQTT_IP, MQTT_PORT) + + # Blocking call that processes network traffic, dispatches callbacks and + # handles reconnecting. + # Other loop*() functions are available that give a threaded interface and a + # manual interface. + mqttClient.loop_forever() + + except (KeyboardInterrupt, Exception) as e: + print "Exception in MQTTServerThread (either KeyboardInterrupt or Other):" + print str(e) + + mqttClient.disconnect() + print time.asctime(), "Connection to Broker closed - %s:%s" % (MQTT_IP, MQTT_PORT) + print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' + pass + + +if __name__ == '__main__': + iotUtils.setUpGPIOPins() + main() + diff --git a/modules/distribution/src/sketches/firealarm/xmppServer.py b/modules/distribution/src/sketches/firealarm/xmppServer.py new file mode 100644 index 00000000..27f74846 --- /dev/null +++ b/modules/distribution/src/sketches/firealarm/xmppServer.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +""" + +import sleekxmpp +import getpass +import sys +import ssl, pyasn1 + +from urllib import urlopen +import iotUtils + +# Python versions before 3.0 do not use UTF-8 encoding +# by default. To ensure that Unicode is handled properly +# throughout SleekXMPP, we will set the default encoding +# ourselves to UTF-8. +if sys.version_info < (3, 0): + from sleekxmpp.util.misc_ops import setdefaultencoding + setdefaultencoding('utf8') +else: + raw_input = input + +from sleekxmpp.plugins.xep_0323.device import Device + +class IoT_TestDevice(sleekxmpp.ClientXMPP): + """ + A simple IoT device that can act as server or client + """ + def __init__(self, jid, password): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + self.add_event_handler("session_start", self.session_start) + self.add_event_handler("message", self.message) + self.device=None + self.releaseMe=False + self.beServer=True + + def beClientOrServer(self,server=True,clientJID=None ): + self.beServer=True + + def testForRelease(self): + # todo thread safe + return self.releaseMe + + def doReleaseMe(self): + # todo thread safe + self.releaseMe=True + + def addDevice(self, device): + self.device=device + + def session_start(self, event): + self.send_presence() + self.get_roster() + # tell your preffered friend that you are alive + #self.send_message(mto='jocke@jabber.sust.se', mbody=self.boundjid.bare +' is now online use xep_323 stanza to talk to me') + + def message(self, msg): + if msg['type'] in ('chat', 'normal'): + print ("got normal chat message" + str(msg)) + ip=urlopen('http://icanhazip.com').read() + msg.reply("Hi I am " + self.boundjid.full + " and I am on IP " + ip).send() + else: + print ("got unknown message type %s", str(msg['type'])) + +class TheDevice(Device): + """ + This is the actual device object that you will use to get information from your real hardware + You will be called in the refresh method when someone is requesting information from you + """ + def __init__(self,nodeId): + Device.__init__(self,nodeId) + + def refresh(self,fields): + """ + the implementation of the refresh method + """ +# global LAST_TEMP + #self._set_momentary_timestamp(self._get_timestamp()) + #self._add_field_momentary_data(self, "Temperature", self.counter) + + self._add_field(name="Temperature", typename="numeric", unit="C") + self._set_momentary_timestamp(self._get_timestamp()) + self._add_field_momentary_data("Temperature", str(iotUtils.LAST_TEMP), flags={"automaticReadout": "true"}) + +def main(): + XMPP_ENDP = iotUtils.XMPP_EP.split(":")[0] + + XMPP_OWN = iotUtils.DEVICE_OWNER + XMPP_JID = iotUtils.DEVICE_ID + "@" + XMPP_ENDP + "/raspi" + XMPP_PWD = iotUtils.AUTH_TOKEN + + print XMPP_OWN + print XMPP_JID + print XMPP_PWD + xmpp = IoT_TestDevice(XMPP_JID,XMPP_PWD) + + xmpp.ssl_version = ssl.PROTOCOL_SSLv3 + + xmpp.register_plugin('xep_0030') + xmpp.register_plugin('xep_0323') + xmpp.register_plugin('xep_0325') + + if XMPP_OWN: + # xmpp['xep_0030'].add_feature(feature='urn:xmpp:sn', + # node=opts.nodeid, + # jid=xmpp.boundjid.full) + + myDevice = TheDevice(XMPP_OWN) + # myDevice._add_field(name="Relay", typename="numeric", unit="Bool"); + myDevice._add_field(name="Temperature", typename="numeric", unit="C") + myDevice._set_momentary_timestamp("2013-03-07T16:24:30") + myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"}) + + xmpp['xep_0323'].register_node(nodeId=XMPP_OWN, device=myDevice, commTimeout=10) + xmpp.beClientOrServer(server=True) + + while not(xmpp.testForRelease()): + try: + xmpp.connect() + xmpp.process(block=True) + print ("lost connection") + except Exception as e: + print "Exception in XMPPServerThread (either KeyboardInterrupt or Other):" + print str(e) + +if __name__ == '__main__': + main() +