pax_global_header00006660000000000000000000000064140011035270014503gustar00rootroot0000000000000052 comment=350e3b5f6c332f93c65dc92a648b36a55e2ab4da smbus2-0.4.1/000077500000000000000000000000001400110352700127205ustar00rootroot00000000000000smbus2-0.4.1/.github/000077500000000000000000000000001400110352700142605ustar00rootroot00000000000000smbus2-0.4.1/.github/workflows/000077500000000000000000000000001400110352700163155ustar00rootroot00000000000000smbus2-0.4.1/.github/workflows/codeql-analysis.yml000066400000000000000000000044711400110352700221360ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ master ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '39 16 * * 1' jobs: analyze: name: Analyze runs-on: ubuntu-latest strategy: fail-fast: false matrix: language: [ 'python' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v1 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 smbus2-0.4.1/.github/workflows/python-publish.yml000066400000000000000000000015771400110352700220370ustar00rootroot00000000000000# This workflows will upload a Python Package using Twine when a release is created # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries name: Upload Python Package on: release: types: [created] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.x' - name: Install dependencies run: | python -m pip install --upgrade pip pip install setuptools wheel twine - name: Build and publish env: TWINE_USERNAME: ${{ secrets.PYPI_USER }} TWINE_PASSWORD: ${{ secrets.PYPI_SECRET }} run: | python setup.py sdist python setup.py bdist_wheel --universal twine upload dist/* smbus2-0.4.1/.gitignore000066400000000000000000000001141400110352700147040ustar00rootroot00000000000000.tox/ doc/_build build/ dist/ .vscode/ .idea/ *.pyc *.egg-info __pycache__ smbus2-0.4.1/.travis.yml000066400000000000000000000007761400110352700150430ustar00rootroot00000000000000language: python cache: pip matrix: include: - python: 2.7 env: TOXENV=py27 - python: 3.4 env: TOXENV=py34 - python: 3.5 env: TOXENV=py35 - python: 3.6 env: TOXENV=py36 - python: 3.7 env: TOXENV=py37 - python: 3.8 env: TOXENV=py38 - python: 3.9 env: TOXENV=py39 - python: 3.5 env: TOXENV=qa,doc install: pip install --upgrade setuptools pip tox script: tox -v smbus2-0.4.1/CHANGELOG.md000066400000000000000000000062501400110352700145340ustar00rootroot00000000000000# Changelog Notable changes to the smbus2 project are recorded here. ## [Unreleased] No unreleased updates. ## [0.4.1] - 2021-01-17 ### General - SonarCloud quality checks. - Tests added to the dist package. ## [0.4.0] - 2020-12-05 ### Added - Support for SMBus PEC (Packet Error Checking). - Support for Python 3 type hinting and mypy static type analysis. Type stubs added to the project. ### Removed As of this version the `SMBusWrapper` class is removed and must be replaced with `SMBus`: ```python # No longer valid! with SMBusWrapper(1) as bus: ... # Replace with with SMBus(1) as bus: ... ``` ### General - Added Python 3.8 and 3.9 to Travis-CI build config. - Cleaner docs: - Removed redundant `README.rst`. - `README.md`, `CHANGELOG.md` and `LICENSE` added to dist package. ## [0.3.0] - 2019-09-07 ### Added - Missing SMBus commands added: `process_call`, `write_block_data`, `read_block_data`, `block_process_call`. Note that the latter two are normally not supported by pure i2c-devices. - SMBus.__init__(), SMBus.open(): `bus` can be a file path as well (issue #17). - Added enter/exit handler to `SMBus` class. - Expose enumeration of i2c funcs. Previously, smbus.funcs was defined, but its flags were not exported. All flags moved into the `I2cFunc` class and exported that. - Added convenience features making the `i2c_msg` class easier to work with. ### Deprecation warning - The `SMBusWrapper` class is now considered deprecated. Please replace with `SMBus`. ### Changed - Removed `__slots__` from `i2c_msg` class. - Whole `i2c_msg_iter` class replaced by a simple generator function with same functionality ## [0.2.3] - 2019-01-10 ### Fixed - Incorrect `i2c_msg` created in Python 3.x if str input contains ascii chars >= 128. ## [0.2.2] - 2019-01-03 ### Added - SMBus `write_quick` command. ## [0.2.1] - 2018-06-02 ### Added - Ability to force individual r/w operations. - API docs available on readthedocs.org. ## [0.2.0] - 2017-08-19 ### Added - I2C: Support for i2c_rdwr transactions. ## [0.1.5] - 2017-05-17 ### Added - SMBus support for read and write single bytes without offset/register address. ## [0.1.4] - 2016-09-18 ### Added - Force option for SMBusWrapper class. ## [0.1.3] - 2016-08-14 ### Added - Flag flag for forcing address use even when a driver is already using it. ### Fixed bugs - Accept zero (0) as bus ID. - Save address when setting it. ## 0.1.2 - 2016-04-19 First published version. [Unreleased]: https://github.com/kplindegaard/smbus2/compare/0.4.1...HEAD [0.4.1]: https://github.com/kplindegaard/smbus2/compare/0.4.0...0.4.1 [0.4.0]: https://github.com/kplindegaard/smbus2/compare/0.3.0...0.4.0 [0.3.0]: https://github.com/kplindegaard/smbus2/compare/0.2.3...0.3.0 [0.2.3]: https://github.com/kplindegaard/smbus2/compare/0.2.2...0.2.3 [0.2.2]: https://github.com/kplindegaard/smbus2/compare/0.2.1...0.2.2 [0.2.1]: https://github.com/kplindegaard/smbus2/compare/0.2.0...0.2.1 [0.2.0]: https://github.com/kplindegaard/smbus2/compare/0.1.5...0.2.0 [0.1.5]: https://github.com/kplindegaard/smbus2/compare/0.1.4...0.1.5 [0.1.4]: https://github.com/kplindegaard/smbus2/compare/0.1.3...0.1.4 [0.1.3]: https://github.com/kplindegaard/smbus2/compare/0.1.2...0.1.3 smbus2-0.4.1/LICENSE000066400000000000000000000021011400110352700137170ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2017 Karl-Petter Lindegaard Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. smbus2-0.4.1/MANIFEST.in000066400000000000000000000001031400110352700144500ustar00rootroot00000000000000include CHANGELOG.md include LICENSE include README.md graft tests smbus2-0.4.1/README.md000066400000000000000000000131021400110352700141740ustar00rootroot00000000000000# smbus2 A drop-in replacement for smbus-cffi/smbus-python in pure Python [![Build Status](https://travis-ci.org/kplindegaard/smbus2.svg?branch=master)](https://travis-ci.org/kplindegaard/smbus2) [![Documentation Status](https://readthedocs.org/projects/smbus2/badge/?version=latest)](http://smbus2.readthedocs.io/en/latest/?badge=latest) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=kplindegaard_smbus2&metric=alert_status)](https://sonarcloud.io/dashboard?id=kplindegaard_smbus2) ![Python Verions](https://img.shields.io/pypi/pyversions/smbus2.svg) [![PyPi Version](https://img.shields.io/pypi/v/smbus2.svg)](https://pypi.org/project/smbus2/) [![PyPI - Downloads](https://img.shields.io/pypi/dm/smbus2)](https://pypi.org/project/smbus2/) # Introduction smbus2 is (yet another) pure Python implementation of of the [python-smbus](http://www.lm-sensors.org/browser/i2c-tools/trunk/py-smbus/) package. It was designed from the ground up with two goals in mind: 1. It should be a drop-in replacement of smbus. The syntax shall be the same. 2. Use the inherent i2c structs and unions to a greater extent than other pure Python implementations like [pysmbus](https://github.com/bjornt/pysmbus) does. By doing so, it will be more feature complete and easier to extend. Currently supported features are: * Get i2c capabilities (I2C_FUNCS) * SMBus Packet Error Checking (PEC) support * read_byte * write_byte * read_byte_data * write_byte_data * read_word_data * write_word_data * read_i2c_block_data * write_i2c_block_data * write_quick * process_call * read_block_data * write_block_data * block_process_call * i2c_rdwr - *combined write/read transactions with repeated start* It is developed on Python 2.7 but works without any modifications in Python 3.X too. More information about updates and general changes are recorded in the [change log](https://github.com/kplindegaard/smbus2/blob/master/CHANGELOG.md). # SMBus code examples smbus2 installs next to smbus as the package, so it's not really a 100% replacement. You must change the module name. ## Example 1a: Read a byte from smbus2 import SMBus # Open i2c bus 1 and read one byte from address 80, offset 0 bus = SMBus(1) b = bus.read_byte_data(80, 0) print(b) bus.close() ## Example 1b: Read a byte using 'with' This is the very same example but safer to use since the smbus will be closed automatically when exiting the with block. from smbus2 import SMBus with SMBus(1) as bus: b = bus.read_byte_data(80, 0) print(b) ## Example 1c: Read a byte with PEC enabled Same example with Packet Error Checking enabled. from smbus2 import SMBus with SMBus(1) as bus: bus.pec = 1 # Enable PEC b = bus.read_byte_data(80, 0) print(b) ## Example 2: Read a block of data You can read up to 32 bytes at once. from smbus2 import SMBus with SMBus(1) as bus: # Read a block of 16 bytes from address 80, offset 0 block = bus.read_i2c_block_data(80, 0, 16) # Returned value is a list of 16 bytes print(block) ## Example 3: Write a byte from smbus2 import SMBus with SMBus(1) as bus: # Write a byte to address 80, offset 0 data = 45 bus.write_byte_data(80, 0, data) ## Example 4: Write a block of data It is possible to write 32 bytes at the time, but I have found that error-prone. Write less and add a delay in between if you run into trouble. from smbus2 import SMBus with SMBus(1) as bus: # Write a block of 8 bytes to address 80 from offset 0 data = [1, 2, 3, 4, 5, 6, 7, 8] bus.write_i2c_block_data(80, 0, data) # I2C Starting with v0.2, the smbus2 library also has support for combined read and write transactions. *i2c_rdwr* is not really a SMBus feature but comes in handy when the master needs to: 1. read or write bulks of data larger than SMBus' 32 bytes limit. 1. write some data and then read from the slave with a repeated start and no stop bit between. Each operation is represented by a *i2c_msg* message object. ## Example 5: Single i2c_rdwr from smbus2 import SMBus, i2c_msg with SMBus(1) as bus: # Read 64 bytes from address 80 msg = i2c_msg.read(80, 64) bus.i2c_rdwr(msg) # Write a single byte to address 80 msg = i2c_msg.write(80, [65]) bus.i2c_rdwr(msg) # Write some bytes to address 80 msg = i2c_msg.write(80, [65, 66, 67, 68]) bus.i2c_rdwr(msg) ## Example 6: Dual i2c_rdwr To perform dual operations just add more i2c_msg instances to the bus call: from smbus2 import SMBus, i2c_msg # Single transaction writing two bytes then read two at address 80 write = i2c_msg.write(80, [40, 50]) read = i2c_msg.read(80, 2) with SMBus(1) as bus: bus.i2c_rdwr(write, read) ## Example 7: Access i2c_msg data All data is contained in the i2c_msg instances. Here are some data access alternatives. # 1: Convert message content to list msg = i2c_msg.write(60, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) data = list(msg) # data = [1, 2, 3, ...] print(len(data)) # => 10 # 2: i2c_msg is iterable for value in msg: print(value) # 3: Through i2c_msg properties for k in range(msg.len): print(msg.buf[k]) # Installation instructions From PyPi with `pip`: pip install smbus2 From conda-forge using `conda`: conda install -c conda-forge smbus2 Installation from source code is straight forward: python setup.py install smbus2-0.4.1/doc/000077500000000000000000000000001400110352700134655ustar00rootroot00000000000000smbus2-0.4.1/doc/Makefile000066400000000000000000000011331400110352700151230ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXPROJ = smbus2 SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)smbus2-0.4.1/doc/conf.py000066400000000000000000000122661400110352700147730ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # The MIT License (MIT) # Copyright (c) 2020 Karl-Petter Lindegaard # # smbus2 documentation build configuration file. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert(0, os.path.abspath('..')) from smbus2 import __version__ # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'smbus2' author = 'Karl-Petter Lindegaard' copyright = '2017, {}'.format(author) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = __version__ # The full version, including alpha/beta/rc tags. release = __version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # This is required for the alabaster theme # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars html_sidebars = { '**': [ 'relations.html', # needs 'show_related': True theme option to display 'searchbox.html', ] } # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'smbus2doc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'smbus2.tex', 'smbus2 Documentation', author, 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'smbus2', 'smbus2 Documentation', [author], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'smbus2', 'smbus2 Documentation', author, 'smbus2', 'One line description of project.', 'Miscellaneous'), ] # Configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'https://docs.python.org/2/': None} smbus2-0.4.1/doc/index.rst000066400000000000000000000006511400110352700153300ustar00rootroot00000000000000smbus2 ====== .. image:: https://travis-ci.org/kplindegaard/smbus2.svg?branch=master :target: https://travis-ci.org/kplindegaard/smbus2 .. image:: https://img.shields.io/pypi/pyversions/smbus2.svg :target: https://pypi.python.org/pypi/smbus2 .. image:: https://img.shields.io/pypi/v/smbus2.svg :target: https://pypi.python.org/pypi/smbus2 .. automodule:: smbus2 :members: SMBus, i2c_msg :undoc-members: smbus2-0.4.1/doc/make.bat000066400000000000000000000014521400110352700150740ustar00rootroot00000000000000@ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=. set BUILDDIR=_build set SPHINXPROJ=smbus2 if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% :end popd smbus2-0.4.1/setup.cfg000066400000000000000000000004721400110352700145440ustar00rootroot00000000000000[bdist_wheel] universal = 1 [flake8] ignore = E121, E122, E124, E125, E127, E128, E241, E402, E501, E731, E722 exclude = .ropeproject, .tox, .eggs, # No need to traverse our git directory .git, # There's no value in checking cache directories __pycache__, doc, build, dist smbus2-0.4.1/setup.py000066400000000000000000000040371400110352700144360ustar00rootroot00000000000000# The MIT License (MIT) # Copyright (c) 2017 Karl-Petter Lindegaard import re import os from io import open from setuptools import setup def read_file(fname, encoding='utf-8'): with open(fname, encoding=encoding) as r: return r.read() def find_version(*file_paths): fpath = os.path.join(os.path.dirname(__file__), *file_paths) version_file = read_file(fpath) version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M) if version_match: return version_match.group(1) err_msg = 'Unable to find version string in {}'.format(fpath) raise RuntimeError(err_msg) README = read_file('README.md') version = find_version('smbus2', '__init__.py') test_deps = [ 'mock;python_version<"3.3"', 'nose' ] setup( name="smbus2", version=version, author="Karl-Petter Lindegaard", author_email="kp.lindegaard@gmail.com", description="smbus2 is a drop-in replacement for smbus-cffi/smbus-python in pure Python", license="MIT", keywords=['smbus', 'smbus2', 'python', 'i2c', 'raspberrypi', 'linux'], url="https://github.com/kplindegaard/smbus2", packages=['smbus2'], package_data={'smbus2': ['py.typed', 'smbus2.pyi']}, long_description=README, long_description_content_type="text/markdown", extras_require={ 'docs': [ 'sphinx >= 1.5.3' ], 'qa': [ 'flake8' ], 'test': test_deps }, classifiers=[ "Development Status :: 4 - Beta", "Topic :: Utilities", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9" ], ) smbus2-0.4.1/smbus2/000077500000000000000000000000001400110352700141335ustar00rootroot00000000000000smbus2-0.4.1/smbus2/__init__.py000066400000000000000000000023701400110352700162460ustar00rootroot00000000000000"""smbus2 - A drop-in replacement for smbus-cffi/smbus-python""" # The MIT License (MIT) # Copyright (c) 2020 Karl-Petter Lindegaard # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. from .smbus2 import SMBus, i2c_msg, I2cFunc # noqa: F401 __version__ = "0.4.1" smbus2-0.4.1/smbus2/py.typed000066400000000000000000000000001400110352700156200ustar00rootroot00000000000000smbus2-0.4.1/smbus2/smbus2.py000066400000000000000000000512241400110352700157240ustar00rootroot00000000000000"""smbus2 - A drop-in replacement for smbus-cffi/smbus-python""" # The MIT License (MIT) # Copyright (c) 2020 Karl-Petter Lindegaard # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import os import sys from fcntl import ioctl from ctypes import c_uint32, c_uint8, c_uint16, c_char, POINTER, Structure, Array, Union, create_string_buffer, string_at # Commands from uapi/linux/i2c-dev.h I2C_SLAVE = 0x0703 # Use this slave address I2C_SLAVE_FORCE = 0x0706 # Use this slave address, even if it is already in use by a driver! I2C_FUNCS = 0x0705 # Get the adapter functionality mask I2C_RDWR = 0x0707 # Combined R/W transfer (one STOP only) I2C_SMBUS = 0x0720 # SMBus transfer. Takes pointer to i2c_smbus_ioctl_data I2C_PEC = 0x0708 # != 0 to use PEC with SMBus # SMBus transfer read or write markers from uapi/linux/i2c.h I2C_SMBUS_WRITE = 0 I2C_SMBUS_READ = 1 # Size identifiers uapi/linux/i2c.h I2C_SMBUS_QUICK = 0 I2C_SMBUS_BYTE = 1 I2C_SMBUS_BYTE_DATA = 2 I2C_SMBUS_WORD_DATA = 3 I2C_SMBUS_PROC_CALL = 4 I2C_SMBUS_BLOCK_DATA = 5 # This isn't supported by Pure-I2C drivers with SMBUS emulation, like those in RaspberryPi, OrangePi, etc :( I2C_SMBUS_BLOCK_PROC_CALL = 7 # Like I2C_SMBUS_BLOCK_DATA, it isn't supported by Pure-I2C drivers either. I2C_SMBUS_I2C_BLOCK_DATA = 8 I2C_SMBUS_BLOCK_MAX = 32 # To determine what functionality is present (uapi/linux/i2c.h) try: from enum import IntFlag except ImportError: IntFlag = int class I2cFunc(IntFlag): """ These flags identify the operations supported by an I2C/SMBus device. You can test these flags on your `smbus.funcs` On newer python versions, I2cFunc is an IntFlag enum, but it falls back to class with a bunch of int constants on older releases. """ I2C = 0x00000001 ADDR_10BIT = 0x00000002 PROTOCOL_MANGLING = 0x00000004 # I2C_M_IGNORE_NAK etc. SMBUS_PEC = 0x00000008 NOSTART = 0x00000010 # I2C_M_NOSTART SLAVE = 0x00000020 SMBUS_BLOCK_PROC_CALL = 0x00008000 # SMBus 2.0 SMBUS_QUICK = 0x00010000 SMBUS_READ_BYTE = 0x00020000 SMBUS_WRITE_BYTE = 0x00040000 SMBUS_READ_BYTE_DATA = 0x00080000 SMBUS_WRITE_BYTE_DATA = 0x00100000 SMBUS_READ_WORD_DATA = 0x00200000 SMBUS_WRITE_WORD_DATA = 0x00400000 SMBUS_PROC_CALL = 0x00800000 SMBUS_READ_BLOCK_DATA = 0x01000000 SMBUS_WRITE_BLOCK_DATA = 0x02000000 SMBUS_READ_I2C_BLOCK = 0x04000000 # I2C-like block xfer SMBUS_WRITE_I2C_BLOCK = 0x08000000 # w/ 1-byte reg. addr. SMBUS_HOST_NOTIFY = 0x10000000 SMBUS_BYTE = 0x00060000 SMBUS_BYTE_DATA = 0x00180000 SMBUS_WORD_DATA = 0x00600000 SMBUS_BLOCK_DATA = 0x03000000 SMBUS_I2C_BLOCK = 0x0c000000 SMBUS_EMUL = 0x0eff0008 # i2c_msg flags from uapi/linux/i2c.h I2C_M_RD = 0x0001 # Pointer definitions LP_c_uint8 = POINTER(c_uint8) LP_c_uint16 = POINTER(c_uint16) LP_c_uint32 = POINTER(c_uint32) ############################################################# # Type definitions as in i2c.h class i2c_smbus_data(Array): """ Adaptation of the i2c_smbus_data union in ``i2c.h``. Data for SMBus messages. """ _length_ = I2C_SMBUS_BLOCK_MAX + 2 _type_ = c_uint8 class union_i2c_smbus_data(Union): _fields_ = [ ("byte", c_uint8), ("word", c_uint16), ("block", i2c_smbus_data) ] union_pointer_type = POINTER(union_i2c_smbus_data) class i2c_smbus_ioctl_data(Structure): """ As defined in ``i2c-dev.h``. """ _fields_ = [ ('read_write', c_uint8), ('command', c_uint8), ('size', c_uint32), ('data', union_pointer_type)] __slots__ = [name for name, type in _fields_] @staticmethod def create(read_write=I2C_SMBUS_READ, command=0, size=I2C_SMBUS_BYTE_DATA): u = union_i2c_smbus_data() return i2c_smbus_ioctl_data( read_write=read_write, command=command, size=size, data=union_pointer_type(u)) ############################################################# # Type definitions for i2c_rdwr combined transactions class i2c_msg(Structure): """ As defined in ``i2c.h``. """ _fields_ = [ ('addr', c_uint16), ('flags', c_uint16), ('len', c_uint16), ('buf', POINTER(c_char))] def __iter__(self): """ Iterator / Generator :return: iterates over :py:attr:`buf` :rtype: :py:class:`generator` which returns int values """ idx = 0 while idx < self.len: yield ord(self.buf[idx]) idx += 1 def __len__(self): return self.len def __bytes__(self): return string_at(self.buf, self.len) def __repr__(self): return 'i2c_msg(%d,%d,%r)' % (self.addr, self.flags, self.__bytes__()) def __str__(self): s = self.__bytes__() # Throw away non-decodable bytes s = s.decode(errors="ignore") return s @staticmethod def read(address, length): """ Prepares an i2c read transaction. :param address: Slave address. :type: address: int :param length: Number of bytes to read. :type: length: int :return: New :py:class:`i2c_msg` instance for read operation. :rtype: :py:class:`i2c_msg` """ arr = create_string_buffer(length) return i2c_msg( addr=address, flags=I2C_M_RD, len=length, buf=arr) @staticmethod def write(address, buf): """ Prepares an i2c write transaction. :param address: Slave address. :type address: int :param buf: Bytes to write. Either list of values or str. :type buf: list :return: New :py:class:`i2c_msg` instance for write operation. :rtype: :py:class:`i2c_msg` """ if sys.version_info.major >= 3: if type(buf) is str: buf = bytes(map(ord, buf)) else: buf = bytes(buf) else: if type(buf) is not str: buf = ''.join([chr(x) for x in buf]) arr = create_string_buffer(buf, len(buf)) return i2c_msg( addr=address, flags=0, len=len(arr), buf=arr) class i2c_rdwr_ioctl_data(Structure): """ As defined in ``i2c-dev.h``. """ _fields_ = [ ('msgs', POINTER(i2c_msg)), ('nmsgs', c_uint32) ] __slots__ = [name for name, type in _fields_] @staticmethod def create(*i2c_msg_instances): """ Factory method for creating a i2c_rdwr_ioctl_data struct that can be called with ``ioctl(fd, I2C_RDWR, data)``. :param i2c_msg_instances: Up to 42 i2c_msg instances :rtype: i2c_rdwr_ioctl_data """ n_msg = len(i2c_msg_instances) msg_array = (i2c_msg * n_msg)(*i2c_msg_instances) return i2c_rdwr_ioctl_data( msgs=msg_array, nmsgs=n_msg ) ############################################################# class SMBus(object): def __init__(self, bus=None, force=False): """ Initialize and (optionally) open an i2c bus connection. :param bus: i2c bus number (e.g. 0 or 1) or an absolute file path (e.g. `/dev/i2c-42`). If not given, a subsequent call to ``open()`` is required. :type bus: int or str :param force: force using the slave address even when driver is already using it. :type force: boolean """ self.fd = None self.funcs = I2cFunc(0) if bus is not None: self.open(bus) self.address = None self.force = force self._force_last = None self._pec = 0 def __enter__(self): """Enter handler.""" return self def __exit__(self, exc_type, exc_val, exc_tb): """Exit handler.""" self.close() def open(self, bus): """ Open a given i2c bus. :param bus: i2c bus number (e.g. 0 or 1) or an absolute file path (e.g. '/dev/i2c-42'). :type bus: int or str :raise TypeError: if type(bus) is not in (int, str) """ if isinstance(bus, int): filepath = "/dev/i2c-{}".format(bus) elif isinstance(bus, str): filepath = bus else: raise TypeError("Unexpected type(bus)={}".format(type(bus))) self.fd = os.open(filepath, os.O_RDWR) self.funcs = self._get_funcs() def close(self): """ Close the i2c connection. """ if self.fd: os.close(self.fd) self.fd = None self._pec = 0 def _get_pec(self): return self._pec def enable_pec(self, enable=True): """ Enable/Disable PEC (Packet Error Checking) - SMBus 1.1 and later :param enable: :type enable: Boolean """ if not (self.funcs & I2cFunc.SMBUS_PEC): raise IOError('SMBUS_PEC is not a feature') self._pec = int(enable) ioctl(self.fd, I2C_PEC, self._pec) pec = property(_get_pec, enable_pec) # Drop-in replacement for smbus member "pec" """Get and set SMBus PEC. 0 = disabled (default), 1 = enabled.""" def _set_address(self, address, force=None): """ Set i2c slave address to use for subsequent calls. :param address: :type address: int :param force: :type force: Boolean """ force = force if force is not None else self.force if self.address != address or self._force_last != force: if force is True: ioctl(self.fd, I2C_SLAVE_FORCE, address) else: ioctl(self.fd, I2C_SLAVE, address) self.address = address self._force_last = force def _get_funcs(self): """ Returns a 32-bit value stating supported I2C functions. :rtype: int """ f = c_uint32() ioctl(self.fd, I2C_FUNCS, f) return f.value def write_quick(self, i2c_addr, force=None): """ Perform quick transaction. Throws IOError if unsuccessful. :param i2c_addr: i2c address :type i2c_addr: int :param force: :type force: Boolean """ self._set_address(i2c_addr, force=force) msg = i2c_smbus_ioctl_data.create( read_write=I2C_SMBUS_WRITE, command=0, size=I2C_SMBUS_QUICK) ioctl(self.fd, I2C_SMBUS, msg) def read_byte(self, i2c_addr, force=None): """ Read a single byte from a device. :rtype: int :param i2c_addr: i2c address :type i2c_addr: int :param force: :type force: Boolean :return: Read byte value """ self._set_address(i2c_addr, force=force) msg = i2c_smbus_ioctl_data.create( read_write=I2C_SMBUS_READ, command=0, size=I2C_SMBUS_BYTE ) ioctl(self.fd, I2C_SMBUS, msg) return msg.data.contents.byte def write_byte(self, i2c_addr, value, force=None): """ Write a single byte to a device. :param i2c_addr: i2c address :type i2c_addr: int :param value: value to write :type value: int :param force: :type force: Boolean """ self._set_address(i2c_addr, force=force) msg = i2c_smbus_ioctl_data.create( read_write=I2C_SMBUS_WRITE, command=value, size=I2C_SMBUS_BYTE ) ioctl(self.fd, I2C_SMBUS, msg) def read_byte_data(self, i2c_addr, register, force=None): """ Read a single byte from a designated register. :param i2c_addr: i2c address :type i2c_addr: int :param register: Register to read :type register: int :param force: :type force: Boolean :return: Read byte value :rtype: int """ self._set_address(i2c_addr, force=force) msg = i2c_smbus_ioctl_data.create( read_write=I2C_SMBUS_READ, command=register, size=I2C_SMBUS_BYTE_DATA ) ioctl(self.fd, I2C_SMBUS, msg) return msg.data.contents.byte def write_byte_data(self, i2c_addr, register, value, force=None): """ Write a byte to a given register. :param i2c_addr: i2c address :type i2c_addr: int :param register: Register to write to :type register: int :param value: Byte value to transmit :type value: int :param force: :type force: Boolean :rtype: None """ self._set_address(i2c_addr, force=force) msg = i2c_smbus_ioctl_data.create( read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_BYTE_DATA ) msg.data.contents.byte = value ioctl(self.fd, I2C_SMBUS, msg) def read_word_data(self, i2c_addr, register, force=None): """ Read a single word (2 bytes) from a given register. :param i2c_addr: i2c address :type i2c_addr: int :param register: Register to read :type register: int :param force: :type force: Boolean :return: 2-byte word :rtype: int """ self._set_address(i2c_addr, force=force) msg = i2c_smbus_ioctl_data.create( read_write=I2C_SMBUS_READ, command=register, size=I2C_SMBUS_WORD_DATA ) ioctl(self.fd, I2C_SMBUS, msg) return msg.data.contents.word def write_word_data(self, i2c_addr, register, value, force=None): """ Write a byte to a given register. :param i2c_addr: i2c address :type i2c_addr: int :param register: Register to write to :type register: int :param value: Word value to transmit :type value: int :param force: :type force: Boolean :rtype: None """ self._set_address(i2c_addr, force=force) msg = i2c_smbus_ioctl_data.create( read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_WORD_DATA ) msg.data.contents.word = value ioctl(self.fd, I2C_SMBUS, msg) def process_call(self, i2c_addr, register, value, force=None): """ Executes a SMBus Process Call, sending a 16-bit value and receiving a 16-bit response :param i2c_addr: i2c address :type i2c_addr: int :param register: Register to read/write to :type register: int :param value: Word value to transmit :type value: int :param force: :type force: Boolean :rtype: int """ self._set_address(i2c_addr, force=force) msg = i2c_smbus_ioctl_data.create( read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_PROC_CALL ) msg.data.contents.word = value ioctl(self.fd, I2C_SMBUS, msg) return msg.data.contents.word def read_block_data(self, i2c_addr, register, force=None): """ Read a block of up to 32-bytes from a given register. :param i2c_addr: i2c address :type i2c_addr: int :param register: Start register :type register: int :param force: :type force: Boolean :return: List of bytes :rtype: list """ self._set_address(i2c_addr, force=force) msg = i2c_smbus_ioctl_data.create( read_write=I2C_SMBUS_READ, command=register, size=I2C_SMBUS_BLOCK_DATA ) ioctl(self.fd, I2C_SMBUS, msg) length = msg.data.contents.block[0] return msg.data.contents.block[1:length + 1] def write_block_data(self, i2c_addr, register, data, force=None): """ Write a block of byte data to a given register. :param i2c_addr: i2c address :type i2c_addr: int :param register: Start register :type register: int :param data: List of bytes :type data: list :param force: :type force: Boolean :rtype: None """ length = len(data) if length > I2C_SMBUS_BLOCK_MAX: raise ValueError("Data length cannot exceed %d bytes" % I2C_SMBUS_BLOCK_MAX) self._set_address(i2c_addr, force=force) msg = i2c_smbus_ioctl_data.create( read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_BLOCK_DATA ) msg.data.contents.block[0] = length msg.data.contents.block[1:length + 1] = data ioctl(self.fd, I2C_SMBUS, msg) def block_process_call(self, i2c_addr, register, data, force=None): """ Executes a SMBus Block Process Call, sending a variable-size data block and receiving another variable-size response :param i2c_addr: i2c address :type i2c_addr: int :param register: Register to read/write to :type register: int :param data: List of bytes :type data: list :param force: :type force: Boolean :return: List of bytes :rtype: list """ length = len(data) if length > I2C_SMBUS_BLOCK_MAX: raise ValueError("Data length cannot exceed %d bytes" % I2C_SMBUS_BLOCK_MAX) self._set_address(i2c_addr, force=force) msg = i2c_smbus_ioctl_data.create( read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_BLOCK_PROC_CALL ) msg.data.contents.block[0] = length msg.data.contents.block[1:length + 1] = data ioctl(self.fd, I2C_SMBUS, msg) length = msg.data.contents.block[0] return msg.data.contents.block[1:length + 1] def read_i2c_block_data(self, i2c_addr, register, length, force=None): """ Read a block of byte data from a given register. :param i2c_addr: i2c address :type i2c_addr: int :param register: Start register :type register: int :param length: Desired block length :type length: int :param force: :type force: Boolean :return: List of bytes :rtype: list """ if length > I2C_SMBUS_BLOCK_MAX: raise ValueError("Desired block length over %d bytes" % I2C_SMBUS_BLOCK_MAX) self._set_address(i2c_addr, force=force) msg = i2c_smbus_ioctl_data.create( read_write=I2C_SMBUS_READ, command=register, size=I2C_SMBUS_I2C_BLOCK_DATA ) msg.data.contents.byte = length ioctl(self.fd, I2C_SMBUS, msg) return msg.data.contents.block[1:length + 1] def write_i2c_block_data(self, i2c_addr, register, data, force=None): """ Write a block of byte data to a given register. :param i2c_addr: i2c address :type i2c_addr: int :param register: Start register :type register: int :param data: List of bytes :type data: list :param force: :type force: Boolean :rtype: None """ length = len(data) if length > I2C_SMBUS_BLOCK_MAX: raise ValueError("Data length cannot exceed %d bytes" % I2C_SMBUS_BLOCK_MAX) self._set_address(i2c_addr, force=force) msg = i2c_smbus_ioctl_data.create( read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_I2C_BLOCK_DATA ) msg.data.contents.block[0] = length msg.data.contents.block[1:length + 1] = data ioctl(self.fd, I2C_SMBUS, msg) def i2c_rdwr(self, *i2c_msgs): """ Combine a series of i2c read and write operations in a single transaction (with repeated start bits but no stop bits in between). This method takes i2c_msg instances as input, which must be created first with :py:meth:`i2c_msg.read` or :py:meth:`i2c_msg.write`. :param i2c_msgs: One or more i2c_msg class instances. :type i2c_msgs: i2c_msg :rtype: None """ ioctl_data = i2c_rdwr_ioctl_data.create(*i2c_msgs) ioctl(self.fd, I2C_RDWR, ioctl_data) smbus2-0.4.1/smbus2/smbus2.pyi000066400000000000000000000106551400110352700161000ustar00rootroot00000000000000from enum import IntFlag from typing import Optional, Sequence, List, Type, SupportsBytes, Iterable from typing import Union as _UnionT from types import TracebackType from ctypes import c_uint32, c_uint8, c_uint16, pointer, Structure, Array, Union I2C_SLAVE: int I2C_SLAVE_FORCE: int I2C_FUNCS: int I2C_RDWR: int I2C_SMBUS: int I2C_PEC: int I2C_SMBUS_WRITE: int I2C_SMBUS_READ: int I2C_SMBUS_QUICK: int I2C_SMBUS_BYTE: int I2C_SMBUS_BYTE_DATA: int I2C_SMBUS_WORD_DATA: int I2C_SMBUS_PROC_CALL: int I2C_SMBUS_BLOCK_DATA: int I2C_SMBUS_BLOCK_PROC_CALL: int I2C_SMBUS_I2C_BLOCK_DATA: int I2C_SMBUS_BLOCK_MAX: int class I2cFunc(IntFlag): I2C = ... ADDR_10BIT = ... PROTOCOL_MANGLING = ... SMBUS_PEC = ... NOSTART = ... SLAVE = ... SMBUS_BLOCK_PROC_CALL = ... SMBUS_QUICK = ... SMBUS_READ_BYTE = ... SMBUS_WRITE_BYTE = ... SMBUS_READ_BYTE_DATA = ... SMBUS_WRITE_BYTE_DATA = ... SMBUS_READ_WORD_DATA = ... SMBUS_WRITE_WORD_DATA = ... SMBUS_PROC_CALL = ... SMBUS_READ_BLOCK_DATA = ... SMBUS_WRITE_BLOCK_DATA = ... SMBUS_READ_I2C_BLOCK = ... SMBUS_WRITE_I2C_BLOCK = ... SMBUS_HOST_NOTIFY = ... SMBUS_BYTE = ... SMBUS_BYTE_DATA = ... SMBUS_WORD_DATA = ... SMBUS_BLOCK_DATA = ... SMBUS_I2C_BLOCK = ... SMBUS_EMUL = ... I2C_M_RD: int LP_c_uint8: Type[pointer[c_uint8]] LP_c_uint16: Type[pointer[c_uint16]] LP_c_uint32: Type[pointer[c_uint32]] class i2c_smbus_data(Array): ... class union_i2c_smbus_data(Union): ... union_pointer_type: pointer[union_i2c_smbus_data] class i2c_smbus_ioctl_data(Structure): @staticmethod def create( read_write: int = ..., command: int = ..., size: int = ... ) -> "i2c_smbus_ioctl_data": ... class i2c_msg(Structure): def __iter__(self) -> int: ... def __len__(self) -> int: ... def __bytes__(self) -> str: ... @staticmethod def read(address: int, length: int) -> "i2c_msg": ... @staticmethod def write(address: int, buf: _UnionT[str, Iterable[int], SupportsBytes]) -> "i2c_msg": ... class i2c_rdwr_ioctl_data(Structure): @staticmethod def create(*i2c_msg_instances: Sequence[i2c_msg]) -> "i2c_rdwr_ioctl_data": ... class SMBus: fd: int = ... funcs: I2cFunc = ... address: Optional[int] = ... force: Optional[bool] = ... pec: int = ... def __init__( self, bus: _UnionT[None, int, str] = ..., force: bool = ... ) -> None: ... def __enter__(self) -> "SMBus": ... def __exit__( self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: ... def open(self, bus: _UnionT[int, str]) -> None: ... def close(self) -> None: ... def enable_pec(self, enable: bool) -> None: ... def write_quick(self, i2c_addr: int, force: Optional[bool] = ...) -> None: ... def read_byte(self, i2c_addr: int, force: Optional[bool] = ...) -> int: ... def write_byte( self, i2c_addr: int, value: int, force: Optional[bool] = ... ) -> None: ... def read_byte_data( self, i2c_addr: int, register: int, force: Optional[bool] = ... ) -> int: ... def write_byte_data( self, i2c_addr: int, register: int, value: int, force: Optional[bool] = ... ) -> None: ... def read_word_data( self, i2c_addr: int, register: int, force: Optional[bool] = ... ) -> int: ... def write_word_data( self, i2c_addr: int, register: int, value: int, force: Optional[bool] = ... ) -> None: ... def process_call( self, i2c_addr: int, register: int, value: int, force: Optional[bool] = ... ): ... def read_block_data( self, i2c_addr: int, register: int, force: Optional[bool] = ... ) -> List[int]: ... def write_block_data( self, i2c_addr: int, register: int, data: Sequence[int], force: Optional[bool] = ..., ) -> None: ... def block_process_call( self, i2c_addr: int, register: int, data: Sequence[int], force: Optional[bool] = ..., ) -> List[int]: ... def read_i2c_block_data( self, i2c_addr: int, register: int, length: int, force: Optional[bool] = ... ) -> List[int]: ... def write_i2c_block_data( self, i2c_addr: int, register: int, data: Sequence[int], force: Optional[bool] = ..., ) -> None: ... def i2c_rdwr(self, *i2c_msgs: i2c_msg) -> None: ... smbus2-0.4.1/tests/000077500000000000000000000000001400110352700140625ustar00rootroot00000000000000smbus2-0.4.1/tests/test_datatypes.py000066400000000000000000000077121400110352700175000ustar00rootroot00000000000000# The MIT License (MIT) # Copyright (c) 2020 Karl-Petter Lindegaard # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. from smbus2.smbus2 import i2c_smbus_ioctl_data, union_i2c_smbus_data, i2c_msg, i2c_rdwr_ioctl_data # noqa: F401 from smbus2.smbus2 import I2C_SMBUS_BLOCK_MAX, I2C_SMBUS_READ, I2C_SMBUS_BYTE_DATA import unittest class TestDataTypes(unittest.TestCase): def test_union_i2c_smbus_data(self): u = union_i2c_smbus_data() # Fill array with values 1, 2, ... for k in range(I2C_SMBUS_BLOCK_MAX + 2): u.block[k] = k + 1 # Check that the union works self.assertEqual(u.byte, u.block[0], msg="Byte field differ") self.assertEqual(u.block[16], 17, msg="Array field does not match") # Set byte and se it reflected in the array u.byte = 255 self.assertEqual(u.block[0], 255, msg="Byte field not reflected in array") # Reset array to zeros and check word field for k in range(I2C_SMBUS_BLOCK_MAX + 2): u.block[k] = 0 u.word = 1607 self.assertNotEqual(0, u.word, msg="Word field is zero but should be non-zero") u.word = 0 def test_i2c_smbus_ioctl_data_factory(self): ioctl_msg = i2c_smbus_ioctl_data.create() self.assertEqual(ioctl_msg.read_write, I2C_SMBUS_READ) self.assertEqual(ioctl_msg.size, I2C_SMBUS_BYTE_DATA) # Simple test to check assignment ioctl_msg.data.contents.byte = 25 self.assertEqual(ioctl_msg.data.contents.byte, 25, msg="Get not equal to set") def test_i2c_msg_read(self): msg = i2c_msg.read(80, 10) self.assertEqual(msg.addr, 80) self.assertEqual(msg.len, 10) self.assertEqual(msg.len, len(msg)) self.assertEqual(msg.flags, 1) def test_i2c_msg_write(self): # Create from list buf = [65, 66, 67, 68, 1, 10, 255] msg = i2c_msg.write(81, buf) self.assertEqual(msg.addr, 81) self.assertEqual(msg.len, 7) self.assertEqual(msg.len, len(msg)) self.assertEqual(msg.flags, 0) self.assertListEqual(buf, list(msg)) # Create from str s = "ABCD\x01\n\xFF" msg2 = i2c_msg.write(81, s) self.assertEqual(msg2.addr, msg.addr) self.assertEqual(msg2.len, msg.len) self.assertEqual(msg2.flags, msg.flags) self.assertListEqual(list(msg), list(msg2)) self.assertEqual(str(msg2), "ABCD\x01\n") self.assertGreaterEqual(('%r' % msg2).find(r"ABCD\x01\n\xff"), 0) def test_i2c_msg_iter(self): buf = [10, 11, 12, 13] msg = i2c_msg.write(81, buf) # Convert msg to list and compare msg_list = list(msg) self.assertListEqual(buf, msg_list) # Loop over each message entry i = 0 for value in msg: self.assertEqual(value, buf[i]) i += 1 # Loop over with index and value for i, value in enumerate(msg): self.assertEqual(i + 10, value) smbus2-0.4.1/tests/test_smbus2.py000066400000000000000000000175701400110352700167200ustar00rootroot00000000000000# The MIT License (MIT) # Copyright (c) 2020 Karl-Petter Lindegaard # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import sys import unittest try: import unittest.mock as mock except ImportError: import mock # noqa: F401 from smbus2 import SMBus, i2c_msg, I2cFunc ########################################################################## # Mock open, close and ioctl so we can run our unit tests anywhere. # Required I2C constant definitions repeated I2C_FUNCS = 0x0705 # Get the adapter functionality mask I2C_SMBUS = 0x0720 I2C_SMBUS_WRITE = 0 I2C_SMBUS_READ = 1 I2C_SMBUS_QUICK = 0 I2C_SMBUS_BYTE_DATA = 2 I2C_SMBUS_WORD_DATA = 3 I2C_SMBUS_BLOCK_DATA = 5 # Can't get this one to work on my Raspberry Pi I2C_SMBUS_I2C_BLOCK_DATA = 8 I2C_SMBUS_BLOCK_MAX = 32 MOCK_FD = "Mock file descriptor" # Test buffer for read operations test_buffer = [x for x in range(256)] def bytes_six(lst): """convert a list of int to `bytes` like object""" if sys.version_info.major >= 3: return bytes(lst) else: return ''.join(map(chr, lst)) def mock_open(*args): print("Mocking open: %s" % args[0]) return MOCK_FD def mock_close(*args): assert args[0] == MOCK_FD def mock_read(fd, length): assert fd == MOCK_FD return bytes_six(test_buffer[0:length]) def mock_ioctl(fd, command, msg): print("Mocking ioctl") assert fd == MOCK_FD assert command is not None # Reproduce i2c capability of a Raspberry Pi 3 w/o PEC support if command == I2C_FUNCS: msg.value = 0xeff0001 return # Reproduce ioctl read operations if command == I2C_SMBUS and msg.read_write == I2C_SMBUS_READ: offset = msg.command if msg.size == I2C_SMBUS_BYTE_DATA: msg.data.contents.byte = test_buffer[offset] elif msg.size == I2C_SMBUS_WORD_DATA: msg.data.contents.word = test_buffer[offset + 1] * 256 + test_buffer[offset] elif msg.size == I2C_SMBUS_I2C_BLOCK_DATA: for k in range(msg.data.contents.byte): msg.data.contents.block[k + 1] = test_buffer[offset + k] # Reproduce a failing Quick write transaction if command == I2C_SMBUS and \ msg.read_write == I2C_SMBUS_WRITE and \ msg.size == I2C_SMBUS_QUICK: raise IOError("Mocking SMBus Quick failed") # Override open, close and ioctl with our mock functions open_mock = mock.patch('smbus2.smbus2.os.open', mock_open) close_mock = mock.patch('smbus2.smbus2.os.close', mock_close) ioctl_mock = mock.patch('smbus2.smbus2.ioctl', mock_ioctl) open_mock.start() close_mock.start() ioctl_mock.start() ########################################################################## # Common error messages INCORRECT_LENGTH_MSG = "Result array of incorrect length." # Test cases class TestSMBus(unittest.TestCase): def test_func(self): bus = SMBus(1) print("\nSupported I2C functionality: %x" % bus.funcs) bus.close() def test_enter_exit(self): for id in (1, '/dev/i2c-alias'): with SMBus(id) as bus: self.assertIsNotNone(bus.fd) self.assertIsNone(bus.fd, None) with SMBus() as bus: self.assertIsNone(bus.fd) bus.open(2) self.assertIsNotNone(bus.fd) self.assertIsNone(bus.fd) def test_open_close(self): for id in (1, '/dev/i2c-alias'): bus = SMBus() self.assertIsNone(bus.fd) bus.open(id) self.assertIsNotNone(bus.fd) bus.close() self.assertIsNone(bus.fd) def test_read(self): res = [] res2 = [] res3 = [] bus = SMBus(1) # Read bytes for k in range(2): x = bus.read_byte_data(80, k) res.append(x) self.assertEqual(len(res), 2, msg=INCORRECT_LENGTH_MSG) # Read word x = bus.read_word_data(80, 0) res2.append(x & 255) res2.append(x / 256) self.assertEqual(len(res2), 2, msg=INCORRECT_LENGTH_MSG) self.assertListEqual(res, res2, msg="Byte and word reads differ") # Read block of N bytes n = 2 x = bus.read_i2c_block_data(80, 0, n) res3.extend(x) self.assertEqual(len(res3), n, msg=INCORRECT_LENGTH_MSG) self.assertListEqual(res, res3, msg="Byte and block reads differ") bus.close() def test_quick(self): bus = SMBus(1) self.assertRaises(IOError, bus.write_quick, 80) def test_pec(self): def set_pec(bus, enable=True): bus.pec = enable # Enabling PEC should fail (no mocked PEC support) bus = SMBus(1) self.assertRaises(IOError, set_pec, bus, True) self.assertRaises(IOError, set_pec, bus, 1) self.assertEquals(bus.pec, 0) # Ensure PEC status is reset by close() bus._pec = 1 self.assertEquals(bus.pec, 1) bus.close() self.assertEquals(bus.pec, 0) class TestSMBusWrapper(unittest.TestCase): """Similar test as TestSMBus except it encapsulates it all access within "with" blocks.""" def test_func(self): with SMBus(1) as bus: print("\nSupported I2C functionality: 0x%X" % bus.funcs) self.assertTrue(bus.funcs & I2cFunc.I2C > 0) self.assertTrue(bus.funcs & I2cFunc.SMBUS_QUICK > 0) def test_read(self): res = [] res2 = [] res3 = [] # Read bytes with SMBus(1) as bus: for k in range(2): x = bus.read_byte_data(80, k) res.append(x) self.assertEqual(len(res), 2, msg=INCORRECT_LENGTH_MSG) # Read word with SMBus(1) as bus: x = bus.read_word_data(80, 0) res2.append(x & 255) res2.append(x / 256) self.assertEqual(len(res2), 2, msg=INCORRECT_LENGTH_MSG) self.assertListEqual(res, res2, msg="Byte and word reads differ") # Read block of N bytes n = 2 with SMBus(1) as bus: x = bus.read_i2c_block_data(80, 0, n) res3.extend(x) self.assertEqual(len(res3), n, msg=INCORRECT_LENGTH_MSG) self.assertListEqual(res, res3, msg="Byte and block reads differ") class TestI2CMsg(unittest.TestCase): def test_i2c_msg(self): # 1: Convert message content to list msg = i2c_msg.write(60, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) data = list(msg) self.assertEqual(len(data), 10) # 2: i2c_msg is iterable k = 0 s = 0 for value in msg: k += 1 s += value self.assertEqual(k, 10, msg='Incorrect length') self.assertEqual(s, 55, msg='Incorrect sum') # 3: Through i2c_msg properties k = 0 s = 0 for k in range(msg.len): s += ord(msg.buf[k]) k += 1 self.assertEqual(k, 10, msg='Incorrect length') self.assertEqual(s, 55, msg='Incorrect sum') smbus2-0.4.1/tox.ini000066400000000000000000000005241400110352700142340ustar00rootroot00000000000000[tox] envlist = py{27,34,35,36,37,38,39},qa,doc skip_missing_interpreters = True [testenv] commands = python setup.py install nosetests -v deps = .[test] [testenv:qa] commands = flake8 deps = .[qa] [testenv:doc] commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html changedir = doc deps = .[docs]