pythoncosmxively

Xively: how to activate a device with the python api?


Since COSM has become Xively, a nice device api has been added (or was always there- not sure). The flow is

  1. create product batch with serial numbers
  2. activate devices using some product batch identifiers (?)
  3. start using the device with the obtained feed/api keys

I can't figure out how to do this via the python API- are there any pointers?


Solution

  • This should be added to the library, but for now you can use this code to implement device activation. I have used environment variables to store product secret and device serial, but change that for anything that suites your use case. The only tricky part is that you need to call a2b_hex().

    import xively
    
    from os import environ    
    from hashlib import sha1
    from binascii import a2b_hex
    import hmac
    
    secret = environ['XIVELY_PRODUCT_SECRET']
    serial = environ['XIVELY_DEVICE_SERIAL_NUMBER']
    
    activation = hmac.new(a2b_hex(secret), serial, sha1).hexdigest()
    
    creds = xively.Client(key=None).get('/v2/devices/'+activation+'/activate').json()
    
    xi_feed = xively.XivelyAPIClient(creds['apikey']).feeds.get(creds['feed_id'])
    

    You will also need take care to store the credentials into a file, as a device can be activated only once. You will notice 403 errors if you try to run this code again and again, so do use the Xively developer workbench for deactivating the device under test (you may need to refresh the page).

    Here is a fully working example using config files or environment variables:

    #!/usr/bin/python
    
    from os import environ 
    from hashlib import sha1 
    from binascii import a2b_hex 
    import hmac
    import sys, subprocess
    import ConfigParser
    import xively
    
    CONFIG_FILE = 'xively.conf'
    
    PROVISIONING = 'PROVISIONING'
    
    PROVISIONING_PRODUCT_SECRET = 'PRODUCT_SECRET'
    PROVISIONING_DEVICE_SERIAL = 'DEVICE_SERIAL'
    
    PROVISIONING_FEED_ID = 'FEED_ID'
    PROVISIONING_API_KEY = 'API_KEY'
    
    def get_setting(config, section, key):
        try:
        value = config.get(section, key)
        except:
        print key + " not found in config file. Using environment variable " + key + " instead."
    
        try:
            value = environ[key]
        except:
            print key + " not found in environment."
            raise
        # value defined?
        if not value:
        raise
        return value
    
    def xively_activate_product(secret, serial):
        activation = hmac.new(a2b_hex(secret), serial, sha1).hexdigest()
        creds = xively.Client(key=None).get('/v2/devices/'+activation+'/activate').json()
        return creds 
    
    # main
    config = ConfigParser.RawConfigParser()
    config.read(CONFIG_FILE)
    
    try:
        # see if we already have an api key and feed id
        feed_id = config.get(PROVISIONING, PROVISIONING_FEED_ID)
        api_key = config.get(PROVISIONING, PROVISIONING_API_KEY)
    
        print "Provisioned product details:"
        print "FEED_ID: " + str(feed_id)
        print "API_KEY: " + api_key
    
        # continue working with your activated product here
    
    except:
        print "FEED_ID and API_KEY not found. Activating product now."
    
        # no error handling for secret- it _is_ needed
        try:
        secret = get_setting(config, PROVISIONING, PROVISIONING_PRODUCT_SECRET)
        except:        
        print "Finding " + PROVISIONING_PRODUCT_SECRET + " failed. Giving up."
        sys.exit(1)
    
        try:
        serial = get_setting(config, PROVISIONING, PROVISIONING_DEVICE_SERIAL)
        except:        
        serial = subprocess.check_output("hostname", shell=True)
        if not serial:
            print "Fallback to hostname for " + PROVISIONING_DEVICE_SERIAL + " failed. Giving up."
            sys.exit(1)
    
        try:    
        creds = xively_activate_product(secret, serial)
    
        # check if there were errors
        try:
            creds["errors"]
        except:
            pass
        else:
            print "Product activation failed (" + creds["title"] +": "+ creds["errors"] + ")."
            sys.exit(1)
    
        feed_id = creds['feed_id']
        api_key = creds['apikey']
    
        print "Product activation successful."
        print "FEED_ID: " + str(feed_id)
        print "API_KEY: " + api_key
    
        if not config.has_section(PROVISIONING):
            config.add_section(PROVISIONING)
    
        config.set(PROVISIONING, PROVISIONING_FEED_ID, feed_id)
        config.set(PROVISIONING, PROVISIONING_API_KEY, api_key)
    
        # Writing our configuration file to 'example.cfg'
        with open(CONFIG_FILE, 'wb') as configfile:
            config.write(configfile)
    
        except Exception as e:
        print "Product activation failed (" + str(e) +")."
        sys.exit(1)