Merge pull request #92 from GPrathap/IoTS-1.0.0-M1

updated Raspberry Pi agent: enable secure HTTPS communication
Ruwan 9 years ago
commit e2bbb47b59

@ -225,21 +225,6 @@ public class DroneService {
@Produces("application/octet-stream") @Produces("application/octet-stream")
public Response downloadSketch(@QueryParam("owner") String owner, @QueryParam("deviceName") String customDeviceName, public Response downloadSketch(@QueryParam("owner") String owner, @QueryParam("deviceName") String customDeviceName,
@PathParam("sketch_type") String sketchType) { @PathParam("sketch_type") String sketchType) {
/*try {
ZipArchive zipFile = createDownloadFile(owner, customDeviceName, sketchType);
Response.ResponseBuilder rb = Response.ok(zipFile.getZipFile());
rb.header("Content-Disposition",
"attachment; filename=\"" + zipFile.getFileName() + "\"");
return rb.build();
} catch (IllegalArgumentException ex) {
return Response.status(400).entity(ex.getMessage()).build();//bad request
} catch (DeviceManagementException ex) {
return Response.status(500).entity(ex.getMessage()).build();
} catch (AccessTokenException ex) {
return Response.status(500).entity(ex.getMessage()).build();
} catch (DeviceControllerException ex) {
return Response.status(500).entity(ex.getMessage()).build();
}*/
if (owner == null) { if (owner == null) {
return Response.status(400).build();//bad request return Response.status(400).build();//bad request
} }

@ -75,28 +75,11 @@ public class DroneAnalyzerXMPPConnector extends XmppConnector {
super.closeConnection(); super.closeConnection();
} }
/*public void printRoster(XmppConnector xmppConnection) throws Exception {
if(xmppConnection != null){
Roster roster = xmppConnection.getRoster();
if(roster !=null && roster.getEntries() != null){
Collection<RosterEntry> entries = roster.getEntries();
for (RosterEntry entry : entries) {
System.out.println(String.format("Buddy:%1$s - Status:%2$s",
entry.getName(), entry.getStatus()));
}
}
}else{
System.out.println("There are no users");
}
}*/
@Override @Override
protected void processXMPPMessage(Message xmppMessage) { protected void processXMPPMessage(Message xmppMessage) {
String from = xmppMessage.getFrom(); String from = xmppMessage.getFrom();
String subject = xmppMessage.getSubject(); String subject = xmppMessage.getSubject();
String inbound_message = xmppMessage.getBody(); String inbound_message = xmppMessage.getBody();
//System.out.println("inbound message :"+inbound_message);
int indexOfAt = from.indexOf("@"); int indexOfAt = from.indexOf("@");
int indexOfSlash = from.indexOf("/"); int indexOfSlash = from.indexOf("/");
String deviceId = from.substring(0, indexOfAt); String deviceId = from.substring(0, indexOfAt);
@ -109,7 +92,6 @@ public class DroneAnalyzerXMPPConnector extends XmppConnector {
} }
else { else {
log.error("Message is empty or it is not belongs to "+ DroneConstants.DEVICE_ID); log.error("Message is empty or it is not belongs to "+ DroneConstants.DEVICE_ID);
System.out.println("Message is empty or it is not belongs to "+ DroneConstants.DEVICE_ID);
} }
} }

@ -1,192 +0,0 @@
/*
* 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.
*/
package org.wso2.carbon.device.mgt.iot.droneanalyzer.service.transport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.wso2.carbon.device.mgt.common.DeviceManagementException;
import org.wso2.carbon.device.mgt.iot.droneanalyzer.service.trasformer.MessageTransformer;
import java.util.Collection;
/**
* Created by geesara on 12/7/15.
*/
public class DroneXMPPConnector {
private static Log log = LogFactory.getLog(DroneXMPPConnector.class);
// XmppManager xmppManager;
private MessageTransformer messageController;
private static String xmppServerIP;
private static int xmppServerPort;
private static String xmppAdminUsername;
private static String xmppAdminPassword;
private static String xmppAdminAccountJID;
public DroneXMPPConnector(MessageTransformer messageController) {
this.messageController = messageController;
initConnector();
}
public void initConnector() {
/* xmppServerIP = XmppConfig.getInstance().getXmppServerIP();
xmppAdminUsername = XmppConfig.getInstance().getXmppUsername();
xmppAdminPassword = XmppConfig.getInstance().getXmppPassword();
xmppAdminAccountJID = xmppAdminUsername + "@" + xmppServerIP;*/
xmppServerPort = 5222;
xmppServerIP = "localhost";
xmppAdminUsername = "admin";
xmppAdminPassword = "admin";
xmppAdminAccountJID = xmppAdminUsername + "@" + xmppServerIP;
}
/*public void connectAndLogin() {
try {
super.connectAndLogin(xmppAdminUsername, xmppAdminPassword, null);
super.setMessageFilterOnReceiver(xmppAdminAccountJID);
} catch (DeviceManagementException e) {
log.error("Connect/Login attempt to XMPP Server at: " + xmppServerIP + " failed");
retryXMPPConnection();
}
}*/
private XMPPConnection xmppConnection;
public void connect(String server, int port) throws Exception {
if(xmppConnection == null){
xmppConnection = new XMPPConnection(new ConnectionConfiguration(server, port));
xmppConnection.connect();
}else{
System.out.println("Already user is connected");
}
/*xmppConnection = new XMPPConnection(new ConnectionConfiguration(server, port));
xmppConnection.connect();*/
}
public void disconnect(){
if(xmppConnection != null){
xmppConnection.disconnect();
//interrupt();
}
}
public void login(String username, String password) throws Exception{
printRoster();
connect( xmppServerIP, xmppServerPort);
xmppConnection.login(username, password);
}
public void run(){
try {
System.out.println(xmppAdminAccountJID+xmppAdminPassword);
login(xmppAdminAccountJID, xmppAdminPassword);
System.out.println("Login successful");
listeningForMessages();
} catch (Exception e) {
e.printStackTrace();
}
}
private void listeningForMessages() {
PacketFilter filter = new AndFilter(new PacketTypeFilter(Message.class));
PacketCollector collector = xmppConnection.createPacketCollector(filter);
while (true) {
System.out.println("waiting ...");
Packet packet = collector.nextResult();
if (packet instanceof Message) {
Message inbound_message = (Message) packet;
if (inbound_message != null && inbound_message.getBody() != null){
System.out.println(inbound_message.getBody());
messageController.messageTranslater(inbound_message.getBody());
}
} else {
log.error("Message has been corrupted");
}
}
}
public void printRoster() throws Exception {
if(xmppConnection != null){
Roster roster = xmppConnection.getRoster();
if(roster !=null && roster.getEntries() != null){
Collection<RosterEntry> entries = roster.getEntries();
for (RosterEntry entry : entries) {
System.out.println(String.format("Buddy:%1$s - Status:%2$s",
entry.getName(), entry.getStatus()));
}
}
}else{
System.out.println("There are no users");
}
}
/* private void retryXMPPConnection() {
Thread retryToConnect = new Thread() {
@Override
public void run() {
while (true) {
if (!isConnected()) {
if (log.isDebugEnabled()) {
log.debug("Re-trying to reach XMPP Server....");
}
try {
VirtualFireAlarmXMPPConnector.super.connectAndLogin(xmppAdminUsername,
xmppAdminPassword,
null);
VirtualFireAlarmXMPPConnector.super.setMessageFilterOnReceiver(
xmppAdminAccountJID);
} catch (DeviceManagementException e1) {
if (log.isDebugEnabled()) {
log.debug("Attempt to re-connect to XMPP-Server failed");
}
}
} else {
break;
}
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {
log.error("XMPP: Thread Sleep Interrupt Exception");
}
}
}
};
retryToConnect.setDaemon(true);
retryToConnect.start();
}*/
}

@ -71,26 +71,18 @@ public class MessageTransformer {
try { try {
JsonNode velocity = node.get(MessageConfig.OUT_BASIC_PARAM_VAL).get(MessageConfig.OUT_BASIC_PARAM_VELOCITY); JsonNode velocity = node.get(MessageConfig.OUT_BASIC_PARAM_VAL).get(MessageConfig.OUT_BASIC_PARAM_VELOCITY);
System.out.println("-------1----------");
JsonNode global_location = node.get(MessageConfig.OUT_BASIC_PARAM_VAL).get( JsonNode global_location = node.get(MessageConfig.OUT_BASIC_PARAM_VAL).get(
MessageConfig.OUT_BASIC_PARAM_GLOBAL_LOCATION); MessageConfig.OUT_BASIC_PARAM_GLOBAL_LOCATION);
System.out.println("-------2----------");
JsonNode quatanium_vals = node.get(MessageConfig.OUT_QUATANNIM_VAL); JsonNode quatanium_vals = node.get(MessageConfig.OUT_QUATANNIM_VAL);
JsonNode battery_level = node.get(MessageConfig.OUT_BATTERY_LEVEL); JsonNode battery_level = node.get(MessageConfig.OUT_BATTERY_LEVEL);
System.out.println("-------3----------");
outbound_message = String.format(outbound_message_format_for_iris_drone, sTd(quatanium_vals.get(0)), outbound_message = String.format(outbound_message_format_for_iris_drone, sTd(quatanium_vals.get(0)),
sTd(quatanium_vals.get(1)), sTd(quatanium_vals.get(2)), sTd(velocity.get(0)), sTd(quatanium_vals.get(1)), sTd(quatanium_vals.get(2)), sTd(velocity.get(0)),
sTd(velocity.get(1)), sTd(velocity.get(2)), sTd(global_location.get(0)), sTd(velocity.get(1)), sTd(velocity.get(2)), sTd(global_location.get(0)),
sTd(global_location.get(1)), sTd(global_location.get(2)), sTd(battery_level)); sTd(global_location.get(1)), sTd(global_location.get(2)), sTd(battery_level));
System.out.println("-----------------IRIS_DRONE---------------------"+ outbound_message);
sharedQueue.add(outbound_message); sharedQueue.add(outbound_message);
}catch (Exception e) { }catch (Exception e) {
log.error(e.getMessage()+",\n"+ e); log.error(e.getMessage()+",\n"+ e);
System.out.println( e.getMessage());
} }
} }
@ -104,7 +96,6 @@ public class MessageTransformer {
switch (deviceType.getTextValue()) { switch (deviceType.getTextValue()) {
case MessageConfig.IN_IRIS_DRONE: case MessageConfig.IN_IRIS_DRONE:
System.out.println("incomming message :" + inbound_message);
messageTranslaterForIRISDrone(actualMessage); messageTranslaterForIRISDrone(actualMessage);
break; break;
case MessageConfig.IN_SIMULATOR: case MessageConfig.IN_SIMULATOR:

@ -33,9 +33,6 @@ import org.wso2.carbon.device.mgt.iot.droneanalyzer.service.transport.DroneAnaly
import java.io.File; import java.io.File;
/**
* Created by geesara on 12/9/15.
*/
public class DroneAnalyzerServiceUtils { public class DroneAnalyzerServiceUtils {
private static final String SUPER_TENANT = "carbon.super"; private static final String SUPER_TENANT = "carbon.super";

@ -1,114 +0,0 @@
package org.wso2.carbon.device.mgt.iot.droneanalyzer.service;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.jaxrs.client.WebClient;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.wso2.carbon.device.mgt.common.Device;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Collection;
import java.util.Iterator;
public class DroneServiceTest {
private static org.apache.commons.logging.Log log = LogFactory.getLog(DroneServiceTest.class);
ApplicationContext context;
WebClient client;
@Before
public void init(){
context = new ClassPathXmlApplicationContext("spring-cxf-client.xml");
client = context.getBean("droneClient", WebClient.class);
}
//@Test
public void registerDevice(){
client.path("manager/device/register").accept(MediaType.APPLICATION_JSON_TYPE);
client.query("deviceId", "device7");
client.query("name", "dronetypeOne");
client.query("owner", "DroneOwner");
Response res = client.put(null);
log.info("Response status :"+ res.getStatus());
System.out.println("Response status :"+ res.getStatus());
}
//@Test
public void removeDevice(){
client.path("manager/device/remove/").accept(MediaType.APPLICATION_JSON_TYPE);
client.path("device7");
Response res = client.delete();
log.info("Response status :"+ res.getStatus());
}
//@Test
public void updateDevice(){
client.path("manager/device/update/").accept(MediaType.APPLICATION_JSON_TYPE);
client.path("device2");
client.query("name", "ARDrone");
Response res = client.post(null);
log.info("Response status :"+ res.getStatus());
}
//@Test
public void getDevice(){
client.path("manager/device/").accept(MediaType.APPLICATION_JSON_TYPE);
client.path("device2");
Device res = client.get(Device.class);
log.info("Device name :"+ res.getName());
log.info("Device type :"+ res.getType());
}
//@Test
public void getDroneDevices(){
client.path("manager/devices/").accept(MediaType.APPLICATION_JSON_TYPE);
client.path("DroneOwner");
Collection<? extends Device> res = client.getCollection(Device.class);
Iterator<? extends Device> iterator = res.iterator();
while (iterator.hasNext()) {
Device device = iterator.next();
log.info("Device name :" + device.getName());
log.info("Device type :"+ device.getType());
iterator.remove();
}
}
//@Test
public void downloadSketch(){
client.path("manager/devices/");
client.path("type1");
client.path("download").accept(MediaType.APPLICATION_OCTET_STREAM);
Response res = client.get();
log.info(res.getStatus());
}
//@Test
public void droneController(){
client.path("controller/send_command");
client.query("owner", "DroneOwner");
client.query("deviceId", "device2");
client.query("action", "takeoff");
client.query("speed", 5);
client.query("duration", 56);
client.accept(MediaType.APPLICATION_JSON);
Response res = client.post(null);
System.out.println(res.getStatus());
}
public void generateSketchLink(){
client.path("manager/devices/");
client.path("type1");
client.path("download").accept(MediaType.APPLICATION_OCTET_STREAM);
Response res = client.get();
log.info(res.getStatus());
}
}

@ -1,33 +0,0 @@
package org.wso2.carbon.device.mgt.iot.droneanalyzer.service.transport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.BeforeClass;
import org.wso2.carbon.device.mgt.iot.controlqueue.xmpp.XmppConfig;
import org.wso2.carbon.utils.CarbonUtils;
/**
* Created by geesara on 12/10/15.
*/
public class DroneAnalyzerXMPPConnectorTest {
private static Log log = LogFactory.getLog(DroneAnalyzerXMPPConnectorTest.class);
public DroneAnalyzerXMPPConnector droneAnalyzerXMPPConnector;
@BeforeClass
public void setup(){
//droneAnalyzerXMPPConnector = new DroneAnalyzerXMPPConnector();
//droneAnalyzerXMPPConnector.initConnector();
}
//@Test
public void login(){
// droneAnalyzerXMPPConnector.connectAndLogin();
// log.info("ip address "+XmppConfig.getInstance().getXmppServerIP());
//log.info("path "+ CarbonUtils.getCarbonConfigDirPath());
// log.info("path "+ CarbonUtils.getCarbonHome());
//System.out.println(System.getProperty("carbon.home"));
System.out.println(System.getenv("CARBON_HOME"));
}
}

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="jacksonJsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"></bean>
<util:list id="webClientProviders">
<ref bean="jacksonJsonProvider"/>
</util:list>
<bean id="droneClient" class="org.apache.cxf.jaxrs.client.WebClient"
factory-method="create">
<constructor-arg type="java.lang.String" value="http://localhost:9763/drone_analyzer/DroneAnalyzerServiceUnitManager/"/>
<constructor-arg ref="webClientProviders" />
</bean>
</beans>

@ -23,6 +23,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.apimgt.annotations.api.API; import org.wso2.carbon.apimgt.annotations.api.API;
import org.wso2.carbon.apimgt.annotations.device.DeviceType; import org.wso2.carbon.apimgt.annotations.device.DeviceType;
import org.wso2.carbon.apimgt.annotations.device.feature.Feature;
import org.wso2.carbon.apimgt.webapp.publisher.KeyGenerationUtil; import org.wso2.carbon.apimgt.webapp.publisher.KeyGenerationUtil;
import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier; import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
@ -48,16 +49,7 @@ import org.wso2.carbon.device.mgt.iot.util.ZipUtil;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes; import javax.ws.rs.*;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
@ -432,12 +424,12 @@ public class RaspberryPiService {
* @param state * @param state
* @param response * @param response
*/ */
@Path("controller/bulb/{state}") @Path("controller/bulb")
@POST @POST
public void switchBulb(@HeaderParam("owner") String owner, @Feature( code="bulb", name="Bulb On / Off", type="operation",
@HeaderParam("deviceId") String deviceId, description="Switch on/off Raspberry Pi agent's bulb. (On / Off)")
@HeaderParam("protocol") String protocol, public void switchBulb(@HeaderParam("owner") String owner, @HeaderParam("deviceId") String deviceId,
@PathParam("state") String state, @HeaderParam("protocol") String protocol, @FormParam("state") String state,
@Context HttpServletResponse response) { @Context HttpServletResponse response) {
try { try {
@ -511,6 +503,8 @@ public class RaspberryPiService {
@GET @GET
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Feature( code="readtemperature", name="Temperature", type="monitor",
description="Request temperature reading from Raspberry Pi agent")
public SensorRecord requestTemperature(@HeaderParam("owner") String owner, public SensorRecord requestTemperature(@HeaderParam("owner") String owner,
@HeaderParam("deviceId") String deviceId, @HeaderParam("deviceId") String deviceId,
@HeaderParam("protocol") String protocol, @HeaderParam("protocol") String protocol,

@ -0,0 +1,38 @@
#
# 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.
#
--------------
testRun.sh
--------------
This script is used to run this service in a testing environment. It can be run on a real Raspberry Pi device or
a virtual environment.
To run: sudo ./testRun.sh and follow the instructions.
-------------------
startService.sh
-------------------
After testing, this script can be used to deploy this application as a service on Raspberry Pi which will get loaded
during boot up process.
To run: sudo ./startService.sh
Note: You should have to provide following arguments in RaspberryService.sh as shown below.
DAEMON_OPTS="-l /usr/local/src/RaspberryAgent/RaspberryStats.log -m N -i 56"
-l ----> file to write log
-i ----> time interval between successive data pushes to the wso2 IoT Server
-m ----> weather is going to run on the real device or not

@ -35,7 +35,7 @@ PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
DESC="This service is used to publish events from the Raspberry Pi to the WSO2 Device Cloud" DESC="This service is used to publish events from the Raspberry Pi to the WSO2 Device Cloud"
NAME=RaspberryStats NAME=RaspberryStats
DIR=/usr/local/src/RaspberryAgent DIR=/usr/local/src/RaspberryAgent/src/
DAEMON=$DIR/RaspberryAgent.py DAEMON=$DIR/RaspberryAgent.py
DAEMON_NAME=$NAME DAEMON_NAME=$NAME
SCRIPTNAME=RaspberryService.sh SCRIPTNAME=RaspberryService.sh

@ -15,10 +15,11 @@
# #
# #
[Device-Configurations] [Device-Configurations]
server-name=${SERVER_NAME}
owner=${DEVICE_OWNER} owner=${DEVICE_OWNER}
deviceId=${DEVICE_ID} deviceId=${DEVICE_ID}
device-name=${DEVICE_NAME} device-name=${DEVICE_NAME}
controller-context=/drone_analyzer/controller controller-context=/raspberrypi/controller
https-ep=${HTTPS_EP} https-ep=${HTTPS_EP}
http-ep=${HTTP_EP} http-ep=${HTTP_EP}
apim-ep=${APIM_EP} apim-ep=${APIM_EP}

@ -1,20 +0,0 @@
#
# 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.
#
templates=deviceConfig.properties
zipfilename=RaspberryPi.zip

@ -1,5 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
/** /**
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
@ -40,7 +39,6 @@ logging_enabled = False
LOG_LEVEL = logging.INFO # Could be e.g. "DEBUG" or "WARNING" LOG_LEVEL = logging.INFO # Could be e.g. "DEBUG" or "WARNING"
### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define and parse command line arguments # Define and parse command line arguments
# If the log file is specified on the command line then override the default # If the log file is specified on the command line then override the default
@ -49,7 +47,7 @@ parser = argparse.ArgumentParser(description="Python service to push RPi info to
parser.add_argument("-l", "--log", help="file to write log to (default '" + LOG_FILENAME + "')") parser.add_argument("-l", "--log", help="file to write log to (default '" + LOG_FILENAME + "')")
help_string_for_data_push_interval = "time interval between successive data pushes (default '" + str(PUSH_INTERVAL) + "')" help_string_for_data_push_interval = "time interval between successive data pushes (default '" + str(PUSH_INTERVAL) + "')"
help_string_for_running_mode = "time interval between successive data pushes (default '" + str(PUSH_INTERVAL) + "')" help_string_for_running_mode = "where is going to run on the real device or not"
parser.add_argument("-i", "--interval", type=int, help=help_string_for_data_push_interval) parser.add_argument("-i", "--interval", type=int, help=help_string_for_data_push_interval)
parser.add_argument("-m", "--mode", type=str, help=help_string_for_running_mode) parser.add_argument("-m", "--mode", type=str, help=help_string_for_running_mode)
@ -75,7 +73,7 @@ if args.mode:
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Endpoint specific settings to which the data is pushed # Endpoint specific settings to which the data is pushed
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DC_ENDPOINT = iotUtils.HTTP_EP.split(":") DC_ENDPOINT = iotUtils.HTTPS_EP.split(":")
DC_IP = DC_ENDPOINT[1].replace('//', '') DC_IP = DC_ENDPOINT[1].replace('//', '')
DC_PORT = int(DC_ENDPOINT[2]) DC_PORT = int(DC_ENDPOINT[2])
DC_ENDPOINT_CONTEXT = iotUtils.CONTROLLER_CONTEXT DC_ENDPOINT_CONTEXT = iotUtils.CONTROLLER_CONTEXT
@ -87,9 +85,6 @@ HOST_HTTP_SERVER_PORT = iotUtils.getHTTPServerPort()
HOST_AND_PORT = str(HOST)+ ":" + str(HOST_HTTP_SERVER_PORT) HOST_AND_PORT = str(HOST)+ ":" + str(HOST_HTTP_SERVER_PORT)
### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# A class we can use to capture stdout and sterr in the log # A class we can use to capture stdout and sterr in the log
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -104,8 +99,6 @@ class IOTLogger(object):
self.logger.log(self.level, message.rstrip()) self.logger.log(self.level, message.rstrip())
### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Configure logging to log to a file, # Configure logging to log to a file,
# making a new file at midnight and keeping the last 3 day's data # making a new file at midnight and keeping the last 3 day's data
@ -130,9 +123,12 @@ def configureLogger(loggerName):
# This method registers the DevieIP in the Device-Cloud # This method registers the DevieIP in the Device-Cloud
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def registerDeviceIP(): def registerDeviceIP():
dcConncection = httplib.HTTPConnection(DC_IP, DC_PORT) dcConncection = httplib.HTTPSConnection(host=DC_IP, port=DC_PORT)
#dcConncection = httplib.HTTPConnection(DC_IP, DC_PORT)
dcConncection.set_debuglevel(1) dcConncection.set_debuglevel(1)
dcConncection.connect() dcConncection.connect()
registerURL = str(REGISTER_ENDPOINT) + '/' + str(iotUtils.DEVICE_OWNER) + '/' + str(iotUtils.DEVICE_ID) + '/' + \ registerURL = str(REGISTER_ENDPOINT) + '/' + str(iotUtils.DEVICE_OWNER) + '/' + str(iotUtils.DEVICE_ID) + '/' + \
str(HOST) + '/' + str(HOST_HTTP_SERVER_PORT) + '/' str(HOST) + '/' + str(HOST_HTTP_SERVER_PORT) + '/'
dcConncection.putrequest('POST', registerURL) dcConncection.putrequest('POST', registerURL)
@ -156,11 +152,9 @@ def registerDeviceIP():
# This method connects to the Device-Cloud and pushes data # This method connects to the Device-Cloud and pushes data
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def connectAndPushData(): def connectAndPushData():
dcConnection = httplib.HTTPConnection(DC_IP, DC_PORT) dcConnection = httplib.HTTPSConnection(host=DC_IP, port=DC_PORT)
dcConnection.set_debuglevel(1) dcConnection.set_debuglevel(1)
dcConnection.connect() dcConnection.connect()
request = dcConnection.putrequest('POST', PUSH_ENDPOINT) request = dcConnection.putrequest('POST', PUSH_ENDPOINT)
dcConnection.putheader('Authorization', 'Bearer ' + iotUtils.AUTH_TOKEN) dcConnection.putheader('Authorization', 'Bearer ' + iotUtils.AUTH_TOKEN)
dcConnection.putheader('Content-Type', 'application/json') dcConnection.putheader('Content-Type', 'application/json')
@ -220,6 +214,7 @@ class TemperatureReaderThread(object):
humidity, temperature = iotUtils.generateRandomTemperatureAndHumidityValues() humidity, temperature = iotUtils.generateRandomTemperatureAndHumidityValues()
if temperature != iotUtils.LAST_TEMP: if temperature != iotUtils.LAST_TEMP:
time.sleep(PUSH_INTERVAL)
iotUtils.LAST_TEMP = temperature iotUtils.LAST_TEMP = temperature
connectAndPushData() connectAndPushData()
@ -234,8 +229,6 @@ class TemperatureReaderThread(object):
time.sleep(self.interval) time.sleep(self.interval)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# This is a Thread object for listening for MQTT Messages # This is a Thread object for listening for MQTT Messages
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -249,9 +242,6 @@ class UtilsThread(object):
iotUtils.main() iotUtils.main()
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# This is a Thread object for HTTP-Server that listens for operations on RPi # This is a Thread object for HTTP-Server that listens for operations on RPi
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -265,13 +255,11 @@ class ListenHTTPServerThread(object):
httpServer.main() httpServer.main()
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def get_now():
"get the current date and time as a string"
return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# When sysvinit sends the TERM signal, cleanup before exiting
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def sigterm_handler(_signo, _stack_frame): def sigterm_handler(_signo, _stack_frame):
"When sysvinit sends the TERM signal, cleanup before exiting."
print("[] received signal {}, exiting...".format(_signo)) print("[] received signal {}, exiting...".format(_signo))
sys.exit(0) sys.exit(0)
@ -291,7 +279,6 @@ class ListenXMPPServerThread(object):
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# This is a Thread object for listening for MQTT Messages # This is a Thread object for listening for MQTT Messages
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -314,7 +301,6 @@ def main():
configureLogger("WSO2IOT_RPiStats") configureLogger("WSO2IOT_RPiStats")
if running_mode.RUNNING_MODE == 'N': if running_mode.RUNNING_MODE == 'N':
iotUtils.setUpGPIOPins() iotUtils.setUpGPIOPins()
UtilsThread() UtilsThread()
registerDeviceIP() # Call the register endpoint and register Device IP registerDeviceIP() # Call the register endpoint and register Device IP
TemperatureReaderThread() # initiates and runs the thread to continuously read temperature from DHT Sensor TemperatureReaderThread() # initiates and runs the thread to continuously read temperature from DHT Sensor

@ -1,338 +0,0 @@
#!/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 datetime
import running_mode
PUSH_INTERVAL = 5000 # time interval between successive data pushes in seconds
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Logger defaults
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#LOG_FILENAME = "/usr/local/src/RaspberryAgent/logs/RaspberryStats.log"
LOG_FILENAME = "RaspberryStats.log"
logging_enabled = False
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_for_data_push_interval = "time interval between successive data pushes (default '" + str(PUSH_INTERVAL) + "')"
help_string_for_running_mode = "time interval between successive data pushes (default '" + str(PUSH_INTERVAL) + "')"
parser.add_argument("-i", "--interval", type=int, help=help_string_for_data_push_interval)
parser.add_argument("-m", "--mode", type=str, help=help_string_for_running_mode)
args = parser.parse_args()
if args.log:
LOG_FILENAME = args.log
if args.interval:
PUSH_INTERVAL = args.interval
if args.mode:
running_mode.RUNNING_MODE = args.mode
iotUtils = __import__('iotUtils')
httpServer = __import__('httpServer') # python script used to start a http-server to listen for operations
# (includes the TEMPERATURE global variable)
mqttListener = __import__('mqttListener') # python script used to accept messages via mqtt
#xmppServer = __import__('xmppServer') # python script used to communicate with xmpp server
if running_mode.RUNNING_MODE == 'N':
Adafruit_DHT = __import__('Adafruit_DHT') # Adafruit library required for temperature sensing
### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Endpoint specific settings to which the data is pushed
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DC_ENDPOINT = iotUtils.HTTP_EP.split(":")
DC_IP = DC_ENDPOINT[1].replace('//', '')
DC_PORT = int(DC_ENDPOINT[2])
DC_ENDPOINT_CONTEXT = iotUtils.CONTROLLER_CONTEXT
PUSH_ENDPOINT = str(DC_ENDPOINT_CONTEXT) + '/push_temperature/'
REGISTER_ENDPOINT = str(DC_ENDPOINT_CONTEXT) + '/register'
HOST = iotUtils.getDeviceIP()
HOST_HTTP_SERVER_PORT = iotUtils.getHTTPServerPort()
HOST_AND_PORT = str(HOST)+ ":" + str(HOST_HTTP_SERVER_PORT)
### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 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 = str(REGISTER_ENDPOINT) + '/' + str(iotUtils.DEVICE_OWNER) + '/' + str(iotUtils.DEVICE_ID) + '/' + \
str(HOST) + '/' + str(HOST_HTTP_SERVER_PORT) + '/'
dcConncection.putrequest('POST', registerURL)
dcConncection.putheader('Authorization', 'Bearer ' + iotUtils.AUTH_TOKEN)
dcConncection.endheaders()
dcResponse = dcConncection.getresponse()
print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
print ('RASPBERRY_STATS: ' + str(registerURL))
print ('RASPBERRY_STATS: ' + str(dcResponse.status))
print ('RASPBERRY_STATS: ' + str(dcResponse.reason))
print ('RASPBERRY_STATS: Response Message')
print str(dcResponse.msg)
dcConncection.close()
print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# This method connects to the Device-Cloud and pushes data
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def connectAndPushData():
dcConnection = httplib.HTTPConnection(DC_IP, DC_PORT)
dcConnection.set_debuglevel(1)
dcConnection.connect()
request = dcConnection.putrequest('POST', PUSH_ENDPOINT)
dcConnection.putheader('Authorization', 'Bearer ' + iotUtils.AUTH_TOKEN)
dcConnection.putheader('Content-Type', 'application/json')
### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
### Read the Temperature and Load info of RPi and construct payload
### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rPiTemperature = iotUtils.LAST_TEMP # Push the last read temperature value
PUSH_DATA = iotUtils.DEVICE_INFO + iotUtils.DEVICE_IP.format(ip=HOST_AND_PORT) + iotUtils.DEVICE_DATA.format(
temperature=rPiTemperature)
PUSH_DATA += '}'
dcConnection.putheader('Content-Length', len(PUSH_DATA))
dcConnection.endheaders()
print PUSH_DATA
print '~~~~~~~~~~~~~~~~~~~~~~~~ Pushing Device-Data ~~~~~~~~~~~~~~~~~~~~~~~~~'
dcConnection.send(PUSH_DATA) # Push the data
dcResponse = dcConnection.getresponse()
print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
print ('RASPBERRY_STATS: ' + str(dcResponse.status))
print ('RASPBERRY_STATS: ' + str(dcResponse.reason))
print ('RASPBERRY_STATS: Response Message')
print str(dcResponse.msg)
print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
dcConnection.close()
if (dcResponse.status == 409 or dcResponse.status == 412):
print 'RASPBERRY_STATS: Re-registering Device IP'
registerDeviceIP()
### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# This is a Thread object for reading temperature continuously
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class TemperatureReaderThread(object):
def __init__(self):
if running_mode.RUNNING_MODE == 'N':
self.interval = iotUtils.TEMPERATURE_READING_INTERVAL_REAL_MODE
else:
self.interval = iotUtils.TEMPERATURE_READING_INTERVAL_VIRTUAL_MODE
thread = threading.Thread(target=self.run, args=())
thread.daemon = True # Daemonize thread
thread.start() # Start the execution
def run(self):
# 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:
if running_mode.RUNNING_MODE == 'N':
humidity, temperature = Adafruit_DHT.read_retry(iotUtils.TEMP_SENSOR_TYPE, iotUtils.TEMP_PIN)
else:
humidity, temperature = iotUtils.generateRandomTemperatureAndHumidityValues()
if temperature != iotUtils.LAST_TEMP:
iotUtils.LAST_TEMP = temperature
connectAndPushData()
iotUtils.LAST_TEMP = temperature
print 'RASPBERRY_STATS: Temp={0:0.1f}*C Humidity={1:0.1f}%'.format(temperature, humidity)
except Exception, e:
print "RASPBERRY_STATS: Exception in TempReaderThread: Could not successfully read Temperature"
print ("RASPBERRY_STATS: " + 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()
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def get_now():
"get the current date and time as a string"
return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
def sigterm_handler(_signo, _stack_frame):
"When sysvinit sends the TERM signal, cleanup before exiting."
print("[] received signal {}, exiting...".format(_signo))
sys.exit(0)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 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):
pass
#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()
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
signal.signal(signal.SIGTERM, sigterm_handler)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# The Main method of the RPi Agent
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def main():
configureLogger("WSO2IOT_RPiStats")
if running_mode.RUNNING_MODE == 'N':
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 "RASPBERRY_STATS: Exception in RaspberryAgentThread (either KeyboardInterrupt or Other)"
print ("RASPBERRY_STATS: " + str(e))
print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
pass
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if __name__ == "__main__":
main()

@ -16,17 +16,17 @@
# #
[Device-Configurations] [Device-Configurations]
owner=admin owner=admin
deviceId=1iv9uchjd5lq0 deviceId=u856bjb7z4fr
device-name=teddr_1iv9uchjd5lq0 device-name=rsdghoooooooooooooooooooooooooooooooooooooo_u856bjb7z4fr
controller-context=/raspberrypi/RaspberryPiDeviceManager/controller controller-context=/raspberrypi/controller
https-ep=https://192.168.237.1:9443 https-ep=https://192.168.237.1:9443
http-ep=http://192.168.237.1:9763 http-ep=http://192.168.237.1:9763
apim-ep=http://localhost:8280 apim-ep=http://192.168.237.1:9763
mqtt-ep=tcp://204.232.188.214:1883 mqtt-ep=tcp://204.232.188.214:1883
xmpp-ep=http://204.232.188.215:5222 xmpp-ep=http://204.232.188.215:5222
auth-method=token auth-method=token
auth-token=1f8fcefa0bb80aac9ae92e0497b19b94 auth-token=a568d7110ec2f4f4f51623cfbf62b67b
refresh-token=d83c134e2038f4ef37d545c23b3be768 refresh-token=d8d1feb54fb2b2fed5436299c64ceaef
push-interval=15 push-interval=15

@ -24,6 +24,9 @@ import time
import BaseHTTPServer import BaseHTTPServer
import iotUtils import iotUtils
import running_mode import running_mode
import os
import subprocess
import re
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Class that handles HTTP GET requests for operations on the RPi # Class that handles HTTP GET requests for operations on the RPi
@ -40,9 +43,10 @@ class OnRequestListener(BaseHTTPServer.BaseHTTPRequestHandler):
state = request.path.split("/")[2].upper() state = request.path.split("/")[2].upper()
print "HTTP_SERVER: Resource - " + resource print "HTTP_SERVER: Resource - " + resource
if resource == "TEMP": if resource == "TEMPERATURE":
request.send_response(200) request.send_response(200)
request.send_header("Content-type", "text/plain") request.send_header('Content-Type', 'application/json')
request.send_header('Authorization', 'Bearer ' + iotUtils.AUTH_TOKEN)
request.end_headers() request.end_headers()
request.wfile.write(iotUtils.LAST_TEMP) request.wfile.write(iotUtils.LAST_TEMP)
@ -52,7 +56,6 @@ class OnRequestListener(BaseHTTPServer.BaseHTTPRequestHandler):
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Check the URL string of the request and validate # Check the URL string of the request and validate
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -63,8 +66,7 @@ def processURLPath(path):
resource = path.split("/")[1] resource = path.split("/")[1]
if not iequal("BULB", resource) and not iequal("TEMP", resource) and not iequal("FAN", resource) and not iequal( if not iequal("BULB", resource) and not iequal("TEMPERATURE", resource):
"SONAR", resource):
if not "favicon" in resource: if not "favicon" in resource:
print "HTTP_SERVER: Invalid resource - " + resource + " to execute operation" print "HTTP_SERVER: Invalid resource - " + resource + " to execute operation"
return False return False
@ -72,8 +74,6 @@ def processURLPath(path):
return True return True
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Case-Insensitive check on whether two string are similar # Case-Insensitive check on whether two string are similar
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -85,7 +85,6 @@ def iequal(a, b):
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# The Main method of the server script # The Main method of the server script
# This method is invoked from RaspberryStats.py on a new thread # This method is invoked from RaspberryStats.py on a new thread

@ -42,7 +42,7 @@ TEMP_PIN = 4
TEMP_SENSOR_TYPE = 11 TEMP_SENSOR_TYPE = 11
BULB_PIN = 11 # The GPIO Pin# in RPi to which the LED is connected BULB_PIN = 11 # The GPIO Pin# in RPi to which the LED is connected
HTTP_SERVER_PORT = 80 # http server port which is listning on HTTP_SERVER_PORT = 5678 # http server port which is listning on
global GPIO global GPIO
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -62,6 +62,7 @@ CONTROLLER_CONTEXT = configParser.get('Device-Configurations', 'controller-conte
DEVICE_INFO = '{"owner":"' + DEVICE_OWNER + '","deviceId":"' + DEVICE_ID + '","reply":' DEVICE_INFO = '{"owner":"' + DEVICE_OWNER + '","deviceId":"' + DEVICE_ID + '","reply":'
HTTPS_EP = configParser.get('Device-Configurations', 'https-ep') HTTPS_EP = configParser.get('Device-Configurations', 'https-ep')
HTTP_EP = configParser.get('Device-Configurations', 'http-ep') HTTP_EP = configParser.get('Device-Configurations', 'http-ep')
APIM_EP = configParser.get('Device-Configurations', 'apim-ep')
DEVICE_IP = '"{ip}","value":' DEVICE_IP = '"{ip}","value":'
DEVICE_DATA = '"{temperature}"' # '"{temperature}:{load}:OFF"' DEVICE_DATA = '"{temperature}"' # '"{temperature}:{load}:OFF"'

@ -28,7 +28,7 @@ then
fi fi
sudo cp $currentDir/deviceConfig.properties $currentDir/src sudo cp $currentDir/deviceConfig.properties $currentDir/src
sudo cp -r $currentDir/src $destination sudo cp -r $currentDir/src $destination
sudo chmod +x $destination/RaspberryAgent.py sudo chmod +x $destination/src/RaspberryAgent.py
sudo update-rc.d -f RaspberryService.sh remove sudo update-rc.d -f RaspberryService.sh remove
sudo cp $currentDir/RaspberryService.sh /etc/init.d sudo cp $currentDir/RaspberryService.sh /etc/init.d
sudo chmod +x /etc/init.d/RaspberryService.sh sudo chmod +x /etc/init.d/RaspberryService.sh

@ -103,8 +103,9 @@ while true; do
* ) echo "Please answer yes or no."; * ) echo "Please answer yes or no.";
esac esac
done done
cp deviceConfig.properties ./src
./src/RaspberryStats.py -i $input -m $mode chmod +x ./src/RaspberryAgent.py
./src/RaspberryAgent.py -i $input -m $mode
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Could not start the service..." echo "Could not start the service..."

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFkzCCA3sCBAKkVfcwDQYJKoZIhvcNAQEFBQAwgY0xCzAJBgNVBAYTAlNMMRAw
DgYDVQQIEwdXZXN0ZXJuMRAwDgYDVQQHEwdDb2xvbWJvMQ0wCwYDVQQKEwRXU08y
MRQwEgYDVQQLEwtFbmdpbmVlcmluZzESMBAGA1UEAxMJbG9jYWxob3N0MSEwHwYJ
KoZIhvcNAQkBFhJpb3RzZXJ2ZXJAd3NvMi5jb20wHhcNMTUxMjE3MTMxMTA0WhcN
MTcxMjE2MTMxMTA0WjCBjTELMAkGA1UEBhMCU0wxEDAOBgNVBAgTB1dlc3Rlcm4x
EDAOBgNVBAcTB0NvbG9tYm8xDTALBgNVBAoTBFdTTzIxFDASBgNVBAsTC0VuZ2lu
ZWVyaW5nMRIwEAYDVQQDEwlsb2NhbGhvc3QxITAfBgkqhkiG9w0BCQEWEmlvdHNl
cnZlckB3c28yLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALki
GVQ9tZOKIi/gD/toV+enq+neqOBGYQ8Fq/ABOWnK2QpGWm81+Rets5GbQ6W//D8C
5TOBGqK7z+LAgdmILr1XLkvrXWoan0GPdDJ1wpc2/6XDZvM5f7Y8cmRqVPJv7AF+
ImgF9dqv97gYCiujy+nNHd5Nk/60pco2LBV5SyLqqrzKXEnSGrS4zoYWpPeJ9YrX
PEkW7A6AxTQK0yU9Ej4TktgafbTueythrLomKiZJj4wPxm2lA2lAZscDdws9NWrI
5z/LUVLbUMxrY10Nig1liX5b1mrUk5bb1d2tqwkPrpRILKoOBJtI674SQS3GziiU
iCJGIO/EGGRn1AJsC/SvnnEez3WKY/DgJ6102MWK/yWtY8NYHUX2anwMBS7UpT5A
4BXdsfBz3R+iPF99FxdAGGsS4GQuuPocZaycLqoPCxpTSSxBsKMUcKpn3yaiQRd6
uDuiTNt7odDOQj0Tno7uokh/HILgbzvj9EExDOsdwLVvqYmUHBPeLmiICWXfi4ky
H/twPOZtV9eVnfWYx5Kwg+2Y4fIb3q4ABr0hzxaMYHQo6NOukSH1BcdAWiQIXbSF
FaTZD8p6OfiZpHcQ59HT/Z8GBlCFL2xkYJFmOhXI/Cu+xrcwqEIInv7d8w3eiNQ7
MneomEptLbBk9+kMsP0ubo34oOGHR9qk3Lj580c/AgMBAAEwDQYJKoZIhvcNAQEF
BQADggIBADw70g2/wrgzrAM8OXBlthGbCEaXZpKwq9IJN0qu+/l+PNwF7csQhj+q
W+zMrWaH1DGWJroaei1+NFFrj/pvp61rF/ZeTPGVJd7puCq++SevqIrzKyAEBtwt
pXmcFhBpV/FrQAv3ODOJ3bN2wSRPZHUvARTBB3RaUI06g1jCaBzjDEGoMfSxdr5/
Ty2WxTI9u9RlIs3Q52AiOmROtLPiEQZQIqfNO3cxCEWojHxPqVEZA/kQYy+rryj4
H0zzSrj7QFlQhsMDw5j8bv9AcvTEGmwp29avsgnceDWinI6lwtd8zqh0ZW9QJdH0
BRNCM/EkTlTUHeEg04/sOgOrlWcvEfVxDqNEtbUzU9UFxl0lkQkuRn1UdxZlvhWa
Fnel5iRC9b7OZvi2mkVujLyxEWlJB1tuyMLQxu6PfabBVODP5V8/+uyiiK/gwrB5
rYl8RHxGoznJnI1Y3HVzKlA849CrMBaY5vnhE03cNja7QroPzLmmuXBLk2LbI1lu
5nJAqKpBUPMI/IU3pF4Q7VTD2ZANI+ktGgGlM8AK4OJHWOhj8W289pWTHVjG8syP
LTsaYkhgLjzZl/g9cUwn/96NJNvzd3dkT+7VgE+BJOLofq25CjZcN1M7MhWdl3vb
WNj9vzL0+FCnwca8UecfvFS39PIekIvqbtP+Gw8NiYOUGIllZ0JH
-----END CERTIFICATE-----
Loading…
Cancel
Save