{"id":28198901,"url":"https://github.com/iotile/python_iotile_cloud","last_synced_at":"2025-05-16T18:14:26.434Z","repository":{"id":62571458,"uuid":"67258159","full_name":"iotile/python_iotile_cloud","owner":"iotile","description":"A python library for interacting with IOTile Cloud by Arch Systems","archived":false,"fork":false,"pushed_at":"2023-04-17T11:19:21.000Z","size":151,"stargazers_count":3,"open_issues_count":5,"forks_count":0,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-05-15T06:41:54.523Z","etag":null,"topics":["iotile","iotile-cloud","python2","python3"],"latest_commit_sha":null,"homepage":"https://iotile.cloud","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iotile.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-09-02T22:12:01.000Z","updated_at":"2020-09-10T23:48:17.000Z","dependencies_parsed_at":"2022-11-03T16:31:18.398Z","dependency_job_id":null,"html_url":"https://github.com/iotile/python_iotile_cloud","commit_stats":null,"previous_names":[],"tags_count":51,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iotile%2Fpython_iotile_cloud","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iotile%2Fpython_iotile_cloud/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iotile%2Fpython_iotile_cloud/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iotile%2Fpython_iotile_cloud/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iotile","download_url":"https://codeload.github.com/iotile/python_iotile_cloud/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254582934,"owners_count":22095520,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["iotile","iotile-cloud","python2","python3"],"created_at":"2025-05-16T18:14:16.534Z","updated_at":"2025-05-16T18:14:26.426Z","avatar_url":"https://github.com/iotile.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# IOTile Cloud Python API Package\n\n[![Build Status](https://travis-ci.org/iotile/python_iotile_cloud.svg?branch=master)](https://travis-ci.org/iotile/python_iotile_cloud)\n[![PyPI version](https://img.shields.io/pypi/v/iotile_cloud.svg)](https://pypi.python.org/pypi/iotile-cloud) \n\n\nA python library for interacting with [IOTile Cloud](https://iotile.cloud) Rest API\n\n## Installation\n\nWhen it comes to using Python packages, it is always recommened you use a Python Virtual Env. Using Python 3, you can simply do\n\n```\npython3 -m venv  ~/.virtualenv/iotile-cloud\n```\n\nor follow the many tutorials on the Web to install `virtualenv` on Python 2.\n\nOnce you have set up a virtual, env, simply install the package with:\n\n```\npip install iotile_cloud\n```\n\nPackage is based on https://github.com/samgiles/slumber\n\n## IOTile Cloud Resource Overview\n\nIn a Rest API, Resources represent tables in the database. The following resources are available in **IOTile Cloud**:\n\n- **account**: Represent users. A user only has access to its own user profile\n- **org**: Users belong to Organizations as members. Some of these users can act as admins for the organization.\n- **project**: Organizations contain Projects. Projects group information about a given set of devices.\n- **device**: A device represents a physical IOTile devices (Like POD-1) or virtual devices\n- **variable**: Variables are used to represent the outputs of a device. e.g. If a device has two sensors, you \nmay have Variable `IO 1` and `IO 2`.\n- **stream**: Streams represent a globally unique instance of data comming from a given sensor. \n- **data**: Every Stream represents the time series data. This resource can be used to access this data.\nThis API always requires a `?filter=\u003cslug\u003e` to filter the data, where the slud is one of the universally unique global IDs\n\n### Globally Unique IDs\n\nMost of the key records in the database use a universally unique ID, in the form of an ID Slug. We borrow the term slug\nfrom blogging systems because we use it the same way to create unique but readable URLs.\n\nThe following are the resources that use a globally unique ID:\n\n- Projects use **p--0000-0001** (Note that project is the one object which the APIs do not use a slug for. Instead, projects require a UUID).\n- Variable **v--0000-0001--5001** represent variable 5001 in project 1\n- Device **d--0000-0000-0000-0001** represent device 1. Note that this is also the Serial Number for the device itself,\nand can be found on each IOTile Device.\n- Stream **s--0000-0001--0000-0000-0000-0002--5001** represent variable 5001 for device 2 in project 1.\n\nYou can see how:\n\n- Slug components are separated by a ‘--’ string\n- A one character letter represents the type of slug: ‘p’, ‘d’, ‘v’ and ‘s’\n- Projects are represented with an 8 character HEX number\n- Devices are represented with a 16 character HEX number\n- Per device Variables are represented with a 4 character HEX number\n- Variable Ids are local to a project and therefore require the project ID to globally uniquify them.\n- Globally unique streams use project, device and variable IDs\n\n\n## User Guide\n\n\n### Login and Logout\n\nThe Api class is used to login and logout from the IOTile Cloud\n\nExample:\n\n```\nfrom iotile_cloud.api.connection import Api\n\nc = Api()\n\nok = c.login(email=args.email, password=password)\nif ok:\n    # Do something\n    \n    c.logout()\n```\n\nIf you have a JWT token, you can skip the login and just set the token:\n\n```\nfrom iotile_cloud.api.connection import Api\n\nc = Api()\n\nc.set_token('big-ugly-token')\n```\n\nYou can use the Api itself to login and get a token:\n\n```\nfrom iotile_cloud.api.connection import Api\n\nc = Api()\n\nok = c.login(email=args.email, password=password)\nif ok:\n    token = c.token\n    # write out token or store in some secret .ini file\n```\n\n### Generic Rest API\n\nThe Api() can be used to access any of the APIs in https://iotile.cloud/api/v1/\n\nThe Api() is generic and therefore will support any future resources supported by the IoTile Cloud Rest API.\n\n```\nfrom iotile_cloud.api.connection import Api\n\napi = Api()\nok = api.login(email='user@example.com', password='my.pass')\n\n## GET http://iotile.cloud/api/v1/project/\n##     Note: Any kwargs passed to get(), post(), put(), delete() will be used as url parameters\napi.org.get()\n\n## POST http://iotile.cloud/api/v1/org/\nnew = api.org.post({\"name\": \"My new Org\"})\n\n## PUT http://iotile.cloud/api/v1/org/{slug}/\napi.org(new[\"slug\"]).put({\"about\": \"About Org\"})\n\nPATCH http://iotile.cloud/api/v1/org/{slug}/\napi.org(new[\"slug\"]).patch({\"about\": \"About new Org\"})\n\n## GET http://iotile.cloud/api/v1/org/{slug}/\napi.org(new[\"slug\"]).get()\n\n## DELETE http://iotile.cloud/api/v1/org/{slug}/\n## NOTE: Not all resources can be deleted by users\napi.org(new[\"slug\"]).delete()\n```\n\nYou can pass arguments to any get() using\n\n```\n# /api/v1/org/\nfor org in c.org.get()['results']:\n   # Pass any arguments as get(foo=1, bar='2'). e.g.\n   # /api/v1/project/?org__slug=\u003cslug\u003e\n   org_projects = c.project.get(org__slug='{0}'.format(org['slug']))\n\n```\n\nYou can also call nested resources/actions like this:\n\n```\n# /api/v1/org/\nfor org in c.org.get()['results']:\n   # /api/v1/org/\u003cslug\u003e/projects\n   org_projects = c.org(org['slug']).projects.get()\n\n```\n\n### Getting Stream Data\n\nYou can use `StreamData` to easily download all or partial stream data.\n\nExamples:\n\n```\n# After calling c.login() or c.set_token()\n\nstream_id = 's--0000-5555--0000-5555-5555-5555--5555'\nstream_data = StreamData(stream_id, c)\n\n# Get last 100 entries\nstream_data.initialize_from_server(lastn=100)\nfor item in stream_data.data:\n    print('{0}: {1}'.format(item['timestamp'], item['value']))\n    \n# Get entries from 2016-1-1 to 2016-1-30 (UTC times are needed)\nstream_data.initialize_from_server(start='2016-01-01T00:00:00.000Z' end='2016-01-30T23:00:00.000Z')\n```\n\nOr just derive from StreamData. For example, the following script will compute Stats\n\n```\nimport getpass\nimport numpy as np\nfrom iotile_cloud.api.connection import Api\nfrom iotile_cloud.stream.data import StreamData\n\n\nemail = 'joe@example.com'\npassword = getpass.getpass()\nstream_id = 's--0000-5555--0000-5555-5555-5555--5555'\nlastn = 1000\n\n\nclass MyStreamData(StreamData):\n\n    def analyze(self):\n        deltas = []\n        for row in self.data:\n            deltas.append(row['value'])\n\n        print('==================================')\n        print('Count = {0}'.format(len(deltas)))\n        print('Mean  = {0}'.format(np.mean(deltas)))\n        print('Max   = {0}'.format(np.max(deltas)))\n        print('Min   = {0}'.format(np.min(deltas)))\n        print('==================================')\n\n\nok = c.login(email=email, password=password)\nif ok:\n    stream_data = MyStreamData(stream_id=stream_id, api=c)\n    stream_data.initialize_from_server(lastn=lastn)\n\n    stream_data.analyze()\n\n    c.logout()\n\n```\n\n### User Reports\n\nPackage includes a simple utility to generate accumulation reports:\n\n```\nfrom pprint import pprint\nfrom iotile_cloud.api.connection import Api\nfrom iotile_cloud.stream.report import AccumulationReportGenerator\n\n# Generate report for all streams from:\nsources = [\n    'p--0000-0001', # Project 1\n    'd--1111',      # Device 0x111\n    's--0000-0002--0000-0000-0000-2222--5001'  # Stream 5001 for device 0x222 in project 2       \n]\nok = c.login(email=email, password=password)\nif ok:\n    gen = AccumulationReportGenerator(c)\n    stats = gen.compute_sum(sources=sources, start=t0, end=t1)\n\n    c.logout()\n```\n\nProduces:\n\n```\n{'streams': {'s--0000-0001--0000-0000-0000-0097--5002': {'sum': 1000.0,\n                                                         'units': 'G'},\n             's--0000-0001--0000-0000-0000-00a0--5001': {'sum': 1500.0,\n                                                         'units': 'G'},\n             's--0000-0003--0000-0000-0000-1111--5001': {'sum': 2000.0,\n                                                         'units': 'G'},\n             's--0000-0002--0000-0000-0000-2222--5001': {'sum': 3000.0,\n                                                         'units': 'G'}},\n 'total': 7500.0}\n\n```\n\n### Uploading a Streamer Report\n\nExample:\n\n```\nfrom iotile_cloud.api.connection import Api\n\nc = Api()\n\nok = c.login(email=args.email, password=password)\nif ok:\n    \n    ts = '{}'.format(datetime.datetime.utcnow().isoformat())\n    resp = c.streamer(action='report').upload_file(filename='path/to/my/file', timestamp=ts)\n```\n\n### Uploading a Stream Event with Data\n\nExample:\n\n```\nfrom iotile_cloud.api.connection import Api\n\nc = Api()\n\nok = c.login(email=args.email, password=password)\nif ok:\n\n    data = {\n       'stream': 's--0000-0001--0000-0000-0000-1111--5020',\n       'timestamp': datetime_to_str(datetime.datetime.utcnow()),\n       'encoded_extra_data': json.dumps({\n           'foo': 'bar'\n       })\n    }\n    resp = c.event.upload.upload_file(filename='path/to/my/file', data=data)\n```\n\n### Globaly unique ID slugs\n\nTo easily handle ID slugs, use the `utils.gid` package:\n\n```\nproject = IOTileProjectSlug(5)\nassert(str(project) == 'p--0000-0005')\n\ndevice = IOTileDeviceSlug(10)\nassert(str(device) == 'd--0000-0000-0000-000a')\n\nvariable = IOTileVariableSlug('5001', project)\nassert(str(variable) == 'v--0000-0005--5001')\n\nid = IOTileStreamSlug()\nid.from_parts(project=project, device=device, variable=variable)\nassert(str(id) == 's--0000-0005--0000-0000-0000-000a--5001')\n\nparts = id.get_parts()\nself.assertEqual(str(parts['project']), str(project))\nself.assertEqual(str(parts['device']), str(device))\nself.assertEqual(str(parts['variable']), str(variable))\n\n# Other forms of use\ndevice = IOTileDeviceSlug('000a)\nassert(str(device) == 'd--0000-0000-0000-000a')\ndevice = IOTileDeviceSlug(d--000a)\nassert(str(device) == 'd--0000-0000-0000-000a')\ndevice = IOTileDeviceSlug(0xa)\nassert(str(device) == 'd--0000-0000-0000-000a')\n```\n\n### BaseMain Utility Class\n\nAs you can see from the examples above, every script is likely to follow the following format:\n\n```\n# Parse arguments from user and get password\n# Login to server\n# Do some real work\n# Logout\n```\n\nTo make it easy to add this boilerplate code, the BaseMain can be used to follow a predefined, opinionated flow\nwhich basically configures the `logging` and `argsparse` python packages with a basic configuration during the \nconstruction. Then the `main()` method runs the following flow, where each function call can be overwritten in your\nown derived class\n\n\n```\n   self.domain = self.get_domain()\n   self.api = Api(self.domain)\n   self.before_login()\n   ok = self.login()\n   if ok:\n       self.after_login()\n       self.logout()\n       self.after_logout()\n```\n\nAn example of how to use this class is shown below:\n\n```\nclass MyScript(BaseMain):\n\n    def add_extra_args(self):\n        # Add extra positional argument (as example)\n        self.parser.add_argument('foo', metavar='foo', type=str, help='RTFM')\n\n    def before_login(self):\n        logger.info('-----------')\n\n    def after_login(self):\n        # Main function to OVERWITE and do real work\n        do_some_real_work(self.api, self.args)\n\n    def login(self):\n        # Add extra message welcoming user\n        ok = super(MyScript, self).login()\n        if ok:\n            logger.info('Welcome {0}'.format(self.args.email))\n        return ok\n\n    def logout(self):\n        # Add extra message to say Goodbye\n        super(MyScript, self).logout()\n        logger.info('Goodbye!')\n\n\nif __name__ == '__main__':\n\n    work = MyScript()\n    work.main()\n```\n\n### Misc Utilities\n\n#### MdoHelper (advance)\n\nIOTile Cloud assumes data is transformed in multiple places using a simple Multiple, Divide and Offset (or MDO) \nset of values. For example, streams use output_units, which include an MDO that can be used to convert the \ninternal value stored on the data stream into this output unit. The MdoHelper is a simple class to help with these\nconverstions.\n\n```\nfrom iotile_cloud.stream.data import StreamData\nfrom iotile_cloud.utils.mdo import MdoHelper\n\nstream = api.stream('s--0000-0001--0000-0000-0000-0001--5001').get()\nunits = stream['output_unit']\nmdo = MdoHelper(m=units.get('m', 1), d=units.get('d', 1), o=units.get('o', 0.0))\n\n# Get unmodified internal data. The 'value' member is based on some internal storage units for the given stream type\nstream_data = StreamRawData(stream['slug'], api)\n\nstream_data.initialize_from_server(lastn=100)\nfor item in stream_data.data:\n    print('{0}: {1}'.format(item['timestamp'], mdo.compute_value(item['value'])))\n```\n\n## Requirements\n\niotile_cloud requires the following modules.\n\n    * Python 2.7+ or 3.4+\n    * requests\n    * python-dateutil\n    \n## Development\n\nTo test, run `python setup.py test` or to run coverage analysis:\n\n```\ncoverage run --source=iotile_cloud setup.py test\ncoverage report -m\n```\n\n## Deployment\n\nTo deploy to pypi:\n\n1. Update `version.py` with new version number\n1. Update `CHANGELOG.md` with description of new release\n1. Run `python setup.py test` to ensure everything is ok\n1. Commit all changes to master (PR is needed)\n1. Once everythin commited, create a new version Tag. Deployment is triggered from that:\n\n```bash\ngit tag -a v0.9.13 -m \"v0.9.13\"\ngit push origin v0.9.13\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiotile%2Fpython_iotile_cloud","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiotile%2Fpython_iotile_cloud","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiotile%2Fpython_iotile_cloud/lists"}