How do I retrieve a file from the Device Cloud with a Digi Python script?

How do I retrieve a file from the Device Cloud with a Digi Python script?

All the examples I have seen only send files out to the Device Cloud.

I know this is possible with an http call. Is there a better way to do this natively on a digi device?

Hello

The following is a python script that I wrote and use for training. It requests a file from the cloud. It then uses DOM to “pick the file apart” The second part might be more than you need. But hopefully this will give you a start.

I don’t know how to attach a file so (unfortunately) I am just dumping the python into the response.

The file name was request_furnace_data_file.py as it was part of an application. You can call it anything you want. The parameters are username, password, device id (the FULL device id) , file name. So it will look for a file associated with a device.


***************************************************************************

Copyright (c) 1996-2014 Digi International Inc.,

All rights not expressly granted are reserved.

This Source Code Form is subject to the terms of the Mozilla Public

License, v. 2.0. If a copy of the MPL was not distributed with this file,

You can obtain one at http://mozilla.org/MPL/2.0/.

Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343

***************************************************************************

-------------------------------------------------

Usage: request_furnace_data_file.py

-------------------------------------------------

import httplib
import base64
import sys
from xml.dom.minidom import parseString
from xml.dom.minidom import Node
import os
import re
from time import sleep, ctime

def Usage():
print 'Usage: request_furnace_data_file.py
’

function handleChildren

purpose walk an XML tree structure and extract all data from between tags

print out tag name and data

This does the lion’s share of the work

def handleChildren(theDOMObject):

create a list of all children of the present node

theChildList = theDOMObject.childNodes

print out what type of node we are looking at

#translateNodeType(theDOMObject.nodeType)

element nodes will have a text node child. That is the guy with the actual data that we are looking for

if theDOMObject.nodeType == Node.ELEMENT_NODE:
print 'Element node ’ + theDOMObject.nodeName
# for each child node look for text non-empty text nodes to print out
for aChildFromThisList in theChildList:
# An element node can have text nodes with no data. They have a carriage return as the text
# lets weed them out, otherwise could be valid text or attribute
# strip will remove all white space, carriage returns and line feeds included. Only print out
# text nodes that have something after being stripped
if aChildFromThisList.nodeType == Node.TEXT_NODE and len(aChildFromThisList.nodeValue.strip()) > 0:
# when we actually print out the data, do not include all of the crud
print 'Value ’ + aChildFromThisList.nodeValue.strip()

if this node has no children and no siblings then there is nothing left to do

if len(theChildList) == 0 and theDOMObject.nextSibling == None:
return

now we nood to look for all children of this node and process them

for theChildNode in theChildList:
if theChildNode.nodeType == Node.ELEMENT_NODE:
# for now we will not worry about attributes
if theChildNode.hasAttributes() == True:
handleAttributes(theChildNode)
#print “would have handled attributes”
# iterate through the children of our present node
thisNodeHasChildren = theChildNode.hasChildNodes()
# only process if this child has children
if thisNodeHasChildren == True:
numChildren = len(theChildNode.childNodes)
tempChildren = theChildNode.childNodes
# look for element nodes that we will handle recursively
if theChildNode.nodeType == Node.ELEMENT_NODE:
handleChildren(theChildNode)

Function: parseTheFileData

Purpose: Take file contents from data retrieved from the cloud

and parse it

Inputs: the raw file dtaa. Probably in XML format

Output: None

def parseTheFileData(theFileData, theTag):
print ’ in parseTheFileData, we are looking starting at ’ + theTag
print theFileData

first step, turn XML file into a tree structure

myXMLDom = parseString(theFileData)

isolate the query setting

memberList = []
memberList = myXMLDom.getElementsByTagName(theTag)
print “Found " + str(len(memberList)) + " " + theTag + " elements”

for each tagName element let handleChildren perform the parsing and printing

for theNextMember in memberList:
print ‘Calling handleChildren from parseTheFileData’
handleChildren(theNextMember)

Function: SendMessage

purpose: format an HTTP request to retrieve a file from the device cloud

Inputs: a user name, a password for that user name, the device id of the device to which the file belongs

(The user will need access to this device), the file nameof the file to be accessed

Outputs: None

def SendMessage(username, password, deviceid, filename):

create HTTP basic authentication string, this consists of

“username:password” base64 encoded

auth = base64.encodestring(“%s:%s”%(username,password))[:-1]

to what URL to send the request with a given HTTP method

webservice = httplib.HTTP(“login.etherios.com”,80)

set up a parameter list for building the URL for the request

myParams = (deviceid, filename)

combine the parameters with the URL to make the full HTTP request string

theRequest = “”“/ws/FileData/~/%s/%s”“” % myParams
print "theRequest holds " + theRequest

add our HTTP request to the HTTP object

webservice.putrequest(“GET”, theRequest)

add the authorization string into the HTTP header

webservice.putheader(“Authorization”, “Basic %s”%auth)

tell the Device Cloud what types of data we’ll accept…include xml

webservice.putheader(“Accept”, “text/html”)
webservice.putheader(“Accept”, “text/plain”)
webservice.putheader(“Accept”, “text/xml”)
webservice.endheaders()

send our request to the device cloud and wait for a response

save the status information seperate from the file contents

the try/except block guards against bad things happening

try:
theStatusCode, theStatusMessage, header = webservice.getreply()
# get the file contents
response_body = webservice.getfile().read()

# for yucks print out the reqponse
print (theStatusCode, theStatusMessage)
# print out the file data
print "File contents:

"
print " "
print response_body
except urllib2.HTTPError, e:
print 'HTTP Error ’ + str(e.code)
except urllib2.URLError, e:
print 'URL Error ’ + str(e.reason)

if theStatusCode == 200:
# if the request was successful, parse the file contents
print ‘got a file from the cloud’
parseTheFileData(response_body, “furnaceDataRecord”)
else:
# if the request failed, just print a message
print ‘Unable to get a file from the cloud’

def main(argv):
try:
iterationCount = 0
#process arguments
count = len(argv);
if count != 4:
Usage()
else:
SendMessage(argv[0], argv[1], argv[2], argv[3])
except KeyboardInterrupt:
print “Execution halted by user action”

if name == “main”:
sys.exit(main(sys.argv[1:]))

1 Like

Thank you that is very helpful.

I guess I am a little confused why the SDK examples to send a file use a Device Cloud library call “dc_data.send_dc_data”. But there doesn’t seem to be any support for other calls to do much else.

Doing everything with HTTP seems kinda weird when the device already has a persistent connection to the Device Cloud…

Are you trying to pull a file down to a PC or are you trying to have a device pull a file from the cloud to itself. The script I provided would be used to pull a file, pushed up by a device to the cloud, from the cloud. If you are looking to have a device pull down a file, then maybe there is another method for doing so.

I would like to pull a file from the Device Cloud to a WR21. Your method would probably work, but I don’t think it’s the best way.

Support says this must be done via an HTTP call and the Device Cloud can not be easily accessed via the existing socket connection for this functionality. Somewhat disappointing, but this is the answer…