├── Adafruit_CCS811 ├── Adafruit_CCS811.py └── __init__.py ├── LICENSE ├── README.md ├── examples └── CCS811_example.py ├── ez_setup.py └── setup.py /Adafruit_CCS811/Adafruit_CCS811.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017 Adafruit Industries 2 | # Author: Dean Miller 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import logging 23 | from Adafruit_bitfield import Adafruit_bitfield 24 | from time import sleep 25 | import math 26 | 27 | CCS811_ADDRESS = 0x5A 28 | 29 | CCS811_STATUS = 0x00 30 | CCS811_MEAS_MODE = 0x01 31 | CCS811_ALG_RESULT_DATA = 0x02 32 | CCS811_RAW_DATA = 0x03 33 | CCS811_ENV_DATA = 0x05 34 | CCS811_NTC = 0x06 35 | CCS811_THRESHOLDS = 0x10 36 | CCS811_BASELINE = 0x11 37 | CCS811_HW_ID = 0x20 38 | CCS811_HW_VERSION = 0x21 39 | CCS811_FW_BOOT_VERSION = 0x23 40 | CCS811_FW_APP_VERSION = 0x24 41 | CCS811_ERROR_ID = 0xE0 42 | CCS811_SW_RESET = 0xFF 43 | 44 | CCS811_BOOTLOADER_APP_ERASE = 0xF1 45 | CCS811_BOOTLOADER_APP_DATA = 0xF2 46 | CCS811_BOOTLOADER_APP_VERIFY = 0xF3 47 | CCS811_BOOTLOADER_APP_START = 0xF4 48 | 49 | CCS811_DRIVE_MODE_IDLE = 0x00 50 | CCS811_DRIVE_MODE_1SEC = 0x01 51 | CCS811_DRIVE_MODE_10SEC = 0x02 52 | CCS811_DRIVE_MODE_60SEC = 0x03 53 | CCS811_DRIVE_MODE_250MS = 0x04 54 | 55 | CCS811_HW_ID_CODE = 0x81 56 | CCS811_REF_RESISTOR = 100000 57 | 58 | class Adafruit_CCS811(object): 59 | def __init__(self, mode=CCS811_DRIVE_MODE_1SEC, address=CCS811_ADDRESS, i2c=None, **kwargs): 60 | self._logger = logging.getLogger('Adafruit_CCS811.CCS811') 61 | # Check that mode is valid. 62 | if mode not in [CCS811_DRIVE_MODE_IDLE, CCS811_DRIVE_MODE_1SEC, CCS811_DRIVE_MODE_10SEC, CCS811_DRIVE_MODE_60SEC, CCS811_DRIVE_MODE_250MS]: 63 | raise ValueError('Unexpected mode value {0}. Set mode to one of CCS811_DRIVE_MODE_IDLE, CCS811_DRIVE_MODE_1SEC, CCS811_DRIVE_MODE_10SEC, CCS811_DRIVE_MODE_60SEC or CCS811_DRIVE_MODE_250MS'.format(mode)) 64 | 65 | # Create I2C device. 66 | if i2c is None: 67 | import Adafruit_GPIO.I2C as I2C 68 | i2c = I2C 69 | self._device = i2c.get_i2c_device(address, **kwargs) 70 | 71 | #set up the registers 72 | self._status = Adafruit_bitfield([('ERROR' , 1), ('unused', 2), ('DATA_READY' , 1), ('APP_VALID', 1), ('unused2' , 2), ('FW_MODE' , 1)]) 73 | 74 | self._meas_mode = Adafruit_bitfield([('unused', 2), ('INT_THRESH', 1), ('INT_DATARDY', 1), ('DRIVE_MODE', 3)]) 75 | 76 | self._error_id = Adafruit_bitfield([('WRITE_REG_INVALID', 1), ('READ_REG_INVALID', 1), ('MEASMODE_INVALID', 1), ('MAX_RESISTANCE', 1), ('HEATER_FAULT', 1), ('HEATER_SUPPLY', 1)]) 77 | 78 | self._TVOC = 0 79 | self._eCO2 = 0 80 | self.tempOffset = 0 81 | 82 | #check that the HW id is correct 83 | if(self._device.readU8(CCS811_HW_ID) != CCS811_HW_ID_CODE): 84 | raise Exception("Device ID returned is not correct! Please check your wiring.") 85 | 86 | #try to start the app 87 | self._device.writeList(CCS811_BOOTLOADER_APP_START, []) 88 | sleep(.1) 89 | 90 | #make sure there are no errors and we have entered application mode 91 | if(self.checkError()): 92 | raise Exception("Device returned an Error! Try removing and reapplying power to the device and running the code again.") 93 | if(not self._status.FW_MODE): 94 | raise Exception("Device did not enter application mode! If you got here, there may be a problem with the firmware on your sensor.") 95 | 96 | self.disableInterrupt() 97 | 98 | #default to read every second 99 | self.setDriveMode(CCS811_DRIVE_MODE_1SEC) 100 | 101 | 102 | def setDriveMode(self, mode): 103 | 104 | self._meas_mode.DRIVE_MODE = mode 105 | self._device.write8(CCS811_MEAS_MODE, self._meas_mode.get()) 106 | 107 | 108 | def enableInterrupt(self): 109 | 110 | self._meas_mode.INT_DATARDY = 1 111 | self._device.write8(CCS811_MEAS_MODE, self._meas_mode.get()) 112 | 113 | 114 | def disableInterrupt(self): 115 | 116 | self._meas_mode.INT_DATARDY = 0 117 | self._device.write8(CCS811_MEAS_MODE, self._meas_mode.get()) 118 | 119 | 120 | def available(self): 121 | 122 | self._status.set(self._device.readU8(CCS811_STATUS)) 123 | if(not self._status.DATA_READY): 124 | return False 125 | else: 126 | return True 127 | 128 | 129 | def readData(self): 130 | 131 | if(not self.available()): 132 | return False 133 | else: 134 | buf = self._device.readList(CCS811_ALG_RESULT_DATA, 8) 135 | 136 | self._eCO2 = (buf[0] << 8) | (buf[1]) 137 | self._TVOC = (buf[2] << 8) | (buf[3]) 138 | 139 | if(self._status.ERROR): 140 | return buf[5] 141 | 142 | else: 143 | return 0 144 | 145 | 146 | 147 | def setEnvironmentalData(self, humidity, temperature): 148 | 149 | ''' Humidity is stored as an unsigned 16 bits in 1/512%RH. The 150 | default value is 50% = 0x64, 0x00. As an example 48.5% 151 | humidity would be 0x61, 0x00.''' 152 | 153 | ''' Temperature is stored as an unsigned 16 bits integer in 1/512 154 | degrees there is an offset: 0 maps to -25C. The default value is 155 | 25C = 0x64, 0x00. As an example 23.5% temperature would be 156 | 0x61, 0x00. 157 | The internal algorithm uses these values (or default values if 158 | not set by the application) to compensate for changes in 159 | relative humidity and ambient temperature.''' 160 | 161 | hum_perc = humidity << 1 162 | 163 | parts = math.fmod(temperature) 164 | fractional = parts[0] 165 | temperature = parts[1] 166 | 167 | temp_high = ((temperature + 25) << 9) 168 | temp_low = ((fractional / 0.001953125) & 0x1FF) 169 | 170 | temp_conv = (temp_high | temp_low) 171 | 172 | buf = [hum_perc, 0x00,((temp_conv >> 8) & 0xFF), (temp_conv & 0xFF)] 173 | 174 | self._device.writeList(CCS811_ENV_DATA, buf) 175 | 176 | 177 | 178 | #calculate temperature based on the NTC register 179 | def calculateTemperature(self): 180 | 181 | buf = self._device.readList(CCS811_NTC, 4) 182 | 183 | vref = (buf[0] << 8) | buf[1] 184 | vrntc = (buf[2] << 8) | buf[3] 185 | rntc = (float(vrntc) * float(CCS811_REF_RESISTOR) / float(vref) ) 186 | 187 | ntc_temp = math.log(rntc / 10000.0) 188 | ntc_temp /= 3380.0 189 | ntc_temp += 1.0 / (25 + 273.15) 190 | ntc_temp = 1.0 / ntc_temp 191 | ntc_temp -= 273.15 192 | return ntc_temp - self.tempOffset 193 | 194 | 195 | def setThresholds(self, low_med, med_high, hysteresis): 196 | 197 | buf = [((low_med >> 8) & 0xF), (low_med & 0xF), ((med_high >> 8) & 0xF), (med_high & 0xF), hysteresis ] 198 | 199 | self._device.writeList(CCS811_THRESHOLDS, buf) 200 | 201 | 202 | def SWReset(self): 203 | 204 | #reset sequence from the datasheet 205 | seq = [0x11, 0xE5, 0x72, 0x8A] 206 | self._device.writeList(CCS811_SW_RESET, seq) 207 | 208 | 209 | def checkError(self): 210 | 211 | self._status.set(self._device.readU8(CCS811_STATUS)) 212 | return self._status.ERROR 213 | 214 | def getTVOC(self): 215 | return self._TVOC 216 | 217 | def geteCO2(self): 218 | return self._eCO2 -------------------------------------------------------------------------------- /Adafruit_CCS811/__init__.py: -------------------------------------------------------------------------------- 1 | from .Adafruit_CCS811 import * -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Adafruit Industries 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DEPRECATED LIBRARY Adafruit CCS811 Python 2 | ========================================= 3 | 4 | This library has been deprecated! 5 | 6 | We are now only using our CircuitPython sensor libraries for Python. 7 | 8 | We are leaving the code up for historical/research purposes but archiving the repository. 9 | 10 | Please use our [Adafruit CircuitPython CSS811](https://github.com/adafruit/Adafruit_CircuitPython_CCS811) library instead! 11 | -------------------------------------------------------------------------------- /examples/CCS811_example.py: -------------------------------------------------------------------------------- 1 | #this example reads and prints CO2 equiv. measurement, TVOC measurement, and temp every 2 seconds 2 | 3 | from time import sleep 4 | from Adafruit_CCS811 import Adafruit_CCS811 5 | 6 | ccs = Adafruit_CCS811() 7 | 8 | while not ccs.available(): 9 | pass 10 | temp = ccs.calculateTemperature() 11 | ccs.tempOffset = temp - 25.0 12 | 13 | while(1): 14 | if ccs.available(): 15 | temp = ccs.calculateTemperature() 16 | if not ccs.readData(): 17 | print "CO2: ", ccs.geteCO2(), "ppm, TVOC: ", ccs.getTVOC(), " temp: ", temp 18 | 19 | else: 20 | print "ERROR!" 21 | while(1): 22 | pass 23 | sleep(2) -------------------------------------------------------------------------------- /ez_setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Bootstrap setuptools installation 3 | To use setuptools in your package's setup.py, include this 4 | file in the same directory and add this to the top of your setup.py:: 5 | from ez_setup import use_setuptools 6 | use_setuptools() 7 | To require a specific version of setuptools, set a download 8 | mirror, or use an alternate download directory, simply supply 9 | the appropriate options to ``use_setuptools()``. 10 | This file can also be run as a script to install or upgrade setuptools. 11 | """ 12 | import os 13 | import shutil 14 | import sys 15 | import tempfile 16 | import zipfile 17 | import optparse 18 | import subprocess 19 | import platform 20 | import textwrap 21 | import contextlib 22 | 23 | from distutils import log 24 | 25 | try: 26 | from site import USER_SITE 27 | except ImportError: 28 | USER_SITE = None 29 | 30 | DEFAULT_VERSION = "3.5.1" 31 | DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/" 32 | 33 | def _python_cmd(*args): 34 | """ 35 | Return True if the command succeeded. 36 | """ 37 | args = (sys.executable,) + args 38 | return subprocess.call(args) == 0 39 | 40 | 41 | def _install(archive_filename, install_args=()): 42 | with archive_context(archive_filename): 43 | # installing 44 | log.warn('Installing Setuptools') 45 | if not _python_cmd('setup.py', 'install', *install_args): 46 | log.warn('Something went wrong during the installation.') 47 | log.warn('See the error message above.') 48 | # exitcode will be 2 49 | return 2 50 | 51 | 52 | def _build_egg(egg, archive_filename, to_dir): 53 | with archive_context(archive_filename): 54 | # building an egg 55 | log.warn('Building a Setuptools egg in %s', to_dir) 56 | _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) 57 | # returning the result 58 | log.warn(egg) 59 | if not os.path.exists(egg): 60 | raise IOError('Could not build the egg.') 61 | 62 | 63 | def get_zip_class(): 64 | """ 65 | Supplement ZipFile class to support context manager for Python 2.6 66 | """ 67 | class ContextualZipFile(zipfile.ZipFile): 68 | def __enter__(self): 69 | return self 70 | def __exit__(self, type, value, traceback): 71 | self.close 72 | return zipfile.ZipFile if hasattr(zipfile.ZipFile, '__exit__') else \ 73 | ContextualZipFile 74 | 75 | 76 | @contextlib.contextmanager 77 | def archive_context(filename): 78 | # extracting the archive 79 | tmpdir = tempfile.mkdtemp() 80 | log.warn('Extracting in %s', tmpdir) 81 | old_wd = os.getcwd() 82 | try: 83 | os.chdir(tmpdir) 84 | with get_zip_class()(filename) as archive: 85 | archive.extractall() 86 | 87 | # going in the directory 88 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) 89 | os.chdir(subdir) 90 | log.warn('Now working in %s', subdir) 91 | yield 92 | 93 | finally: 94 | os.chdir(old_wd) 95 | shutil.rmtree(tmpdir) 96 | 97 | 98 | def _do_download(version, download_base, to_dir, download_delay): 99 | egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg' 100 | % (version, sys.version_info[0], sys.version_info[1])) 101 | if not os.path.exists(egg): 102 | archive = download_setuptools(version, download_base, 103 | to_dir, download_delay) 104 | _build_egg(egg, archive, to_dir) 105 | sys.path.insert(0, egg) 106 | 107 | # Remove previously-imported pkg_resources if present (see 108 | # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details). 109 | if 'pkg_resources' in sys.modules: 110 | del sys.modules['pkg_resources'] 111 | 112 | import setuptools 113 | setuptools.bootstrap_install_from = egg 114 | 115 | 116 | def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 117 | to_dir=os.curdir, download_delay=15): 118 | to_dir = os.path.abspath(to_dir) 119 | rep_modules = 'pkg_resources', 'setuptools' 120 | imported = set(sys.modules).intersection(rep_modules) 121 | try: 122 | import pkg_resources 123 | except ImportError: 124 | return _do_download(version, download_base, to_dir, download_delay) 125 | try: 126 | pkg_resources.require("setuptools>=" + version) 127 | return 128 | except pkg_resources.DistributionNotFound: 129 | return _do_download(version, download_base, to_dir, download_delay) 130 | except pkg_resources.VersionConflict as VC_err: 131 | if imported: 132 | msg = textwrap.dedent(""" 133 | The required version of setuptools (>={version}) is not available, 134 | and can't be installed while this script is running. Please 135 | install a more recent version first, using 136 | 'easy_install -U setuptools'. 137 | (Currently using {VC_err.args[0]!r}) 138 | """).format(VC_err=VC_err, version=version) 139 | sys.stderr.write(msg) 140 | sys.exit(2) 141 | 142 | # otherwise, reload ok 143 | del pkg_resources, sys.modules['pkg_resources'] 144 | return _do_download(version, download_base, to_dir, download_delay) 145 | 146 | def _clean_check(cmd, target): 147 | """ 148 | Run the command to download target. If the command fails, clean up before 149 | re-raising the error. 150 | """ 151 | try: 152 | subprocess.check_call(cmd) 153 | except subprocess.CalledProcessError: 154 | if os.access(target, os.F_OK): 155 | os.unlink(target) 156 | raise 157 | 158 | def download_file_powershell(url, target): 159 | """ 160 | Download the file at url to target using Powershell (which will validate 161 | trust). Raise an exception if the command cannot complete. 162 | """ 163 | target = os.path.abspath(target) 164 | cmd = [ 165 | 'powershell', 166 | '-Command', 167 | "(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)" % vars(), 168 | ] 169 | _clean_check(cmd, target) 170 | 171 | def has_powershell(): 172 | if platform.system() != 'Windows': 173 | return False 174 | cmd = ['powershell', '-Command', 'echo test'] 175 | devnull = open(os.path.devnull, 'wb') 176 | try: 177 | try: 178 | subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 179 | except Exception: 180 | return False 181 | finally: 182 | devnull.close() 183 | return True 184 | 185 | download_file_powershell.viable = has_powershell 186 | 187 | def download_file_curl(url, target): 188 | cmd = ['curl', url, '--silent', '--output', target] 189 | _clean_check(cmd, target) 190 | 191 | def has_curl(): 192 | cmd = ['curl', '--version'] 193 | devnull = open(os.path.devnull, 'wb') 194 | try: 195 | try: 196 | subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 197 | except Exception: 198 | return False 199 | finally: 200 | devnull.close() 201 | return True 202 | 203 | download_file_curl.viable = has_curl 204 | 205 | def download_file_wget(url, target): 206 | cmd = ['wget', url, '--quiet', '--output-document', target] 207 | _clean_check(cmd, target) 208 | 209 | def has_wget(): 210 | cmd = ['wget', '--version'] 211 | devnull = open(os.path.devnull, 'wb') 212 | try: 213 | try: 214 | subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 215 | except Exception: 216 | return False 217 | finally: 218 | devnull.close() 219 | return True 220 | 221 | download_file_wget.viable = has_wget 222 | 223 | def download_file_insecure(url, target): 224 | """ 225 | Use Python to download the file, even though it cannot authenticate the 226 | connection. 227 | """ 228 | try: 229 | from urllib.request import urlopen 230 | except ImportError: 231 | from urllib2 import urlopen 232 | src = dst = None 233 | try: 234 | src = urlopen(url) 235 | # Read/write all in one block, so we don't create a corrupt file 236 | # if the download is interrupted. 237 | data = src.read() 238 | dst = open(target, "wb") 239 | dst.write(data) 240 | finally: 241 | if src: 242 | src.close() 243 | if dst: 244 | dst.close() 245 | 246 | download_file_insecure.viable = lambda: True 247 | 248 | def get_best_downloader(): 249 | downloaders = [ 250 | download_file_powershell, 251 | download_file_curl, 252 | download_file_wget, 253 | download_file_insecure, 254 | ] 255 | 256 | for dl in downloaders: 257 | if dl.viable(): 258 | return dl 259 | 260 | def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 261 | to_dir=os.curdir, delay=15, downloader_factory=get_best_downloader): 262 | """ 263 | Download setuptools from a specified location and return its filename 264 | `version` should be a valid setuptools version number that is available 265 | as an egg for download under the `download_base` URL (which should end 266 | with a '/'). `to_dir` is the directory where the egg will be downloaded. 267 | `delay` is the number of seconds to pause before an actual download 268 | attempt. 269 | ``downloader_factory`` should be a function taking no arguments and 270 | returning a function for downloading a URL to a target. 271 | """ 272 | # making sure we use the absolute path 273 | to_dir = os.path.abspath(to_dir) 274 | zip_name = "setuptools-%s.zip" % version 275 | url = download_base + zip_name 276 | saveto = os.path.join(to_dir, zip_name) 277 | if not os.path.exists(saveto): # Avoid repeated downloads 278 | log.warn("Downloading %s", url) 279 | downloader = downloader_factory() 280 | downloader(url, saveto) 281 | return os.path.realpath(saveto) 282 | 283 | def _build_install_args(options): 284 | """ 285 | Build the arguments to 'python setup.py install' on the setuptools package 286 | """ 287 | return ['--user'] if options.user_install else [] 288 | 289 | def _parse_args(): 290 | """ 291 | Parse the command line for options 292 | """ 293 | parser = optparse.OptionParser() 294 | parser.add_option( 295 | '--user', dest='user_install', action='store_true', default=False, 296 | help='install in user site package (requires Python 2.6 or later)') 297 | parser.add_option( 298 | '--download-base', dest='download_base', metavar="URL", 299 | default=DEFAULT_URL, 300 | help='alternative URL from where to download the setuptools package') 301 | parser.add_option( 302 | '--insecure', dest='downloader_factory', action='store_const', 303 | const=lambda: download_file_insecure, default=get_best_downloader, 304 | help='Use internal, non-validating downloader' 305 | ) 306 | parser.add_option( 307 | '--version', help="Specify which version to download", 308 | default=DEFAULT_VERSION, 309 | ) 310 | options, args = parser.parse_args() 311 | # positional arguments are ignored 312 | return options 313 | 314 | def main(): 315 | """Install or upgrade setuptools and EasyInstall""" 316 | options = _parse_args() 317 | archive = download_setuptools( 318 | version=options.version, 319 | download_base=options.download_base, 320 | downloader_factory=options.downloader_factory, 321 | ) 322 | return _install(archive, _build_install_args(options)) 323 | 324 | if __name__ == '__main__': 325 | sys.exit(main()) -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | try: 2 | # Try using ez_setup to install setuptools if not already installed. 3 | from ez_setup import use_setuptools 4 | use_setuptools() 5 | except ImportError: 6 | # Ignore import error and assume Python 3 which already has setuptools. 7 | pass 8 | 9 | from setuptools import setup, find_packages 10 | 11 | from codecs import open 12 | from os import path 13 | 14 | here = path.abspath(path.dirname(__file__)) 15 | 16 | # Get the long description from the README file 17 | with open(path.join(here, 'README.md'), encoding='utf-8') as f: 18 | long_description = f.read() 19 | 20 | classifiers = ['Development Status :: 4 - Beta', 21 | 'Operating System :: POSIX :: Linux', 22 | 'License :: OSI Approved :: MIT License', 23 | 'Intended Audience :: Developers', 24 | 'Programming Language :: Python :: 2.7', 25 | 'Programming Language :: Python :: 3', 26 | 'Topic :: Software Development', 27 | 'Topic :: System :: Hardware'] 28 | 29 | setup(name = 'Adafruit_CCS811', 30 | version = '0.2.1', 31 | author = 'Dean Miller', 32 | author_email = 'dean@adafruit.com', 33 | description = 'Python library to use the CCS811 air quality sensor with raspberry pi or other linux boards.', 34 | license = 'MIT', 35 | classifiers = classifiers, 36 | url = 'https://github.com/adafruit/Adafruit_CCS811_python/', 37 | dependency_links = ['https://github.com/adafruit/Adafruit_Python_GPIO/tarball/master#egg=Adafruit-GPIO-0.6.5'], 38 | install_requires = ['Adafruit-GPIO>=0.6.5', 'Adafruit-bitfield>=1.5'], 39 | packages = find_packages(), 40 | 41 | long_description = long_description, 42 | long_description_content_type = 'text/markdown') 43 | --------------------------------------------------------------------------------