Skip to main content

hartip-py

CI PyPI Python License Code style: ruff CLA

Pure-Python HART-IP client library. Server functionality is not included.

Supports HART-IP v1 (plaintext, TCP/UDP) and v2 (TLS, Direct PDU, Audit Log). DTLS is not supported — Python's ssl module does not provide DTLS.

Features

  • HART-IP v1: UDP (default) and TCP on port 5094, with session initiate/close, keep-alive and sequence numbers
  • HART-IP v2 TLS: STARTTLS upgrade (HCF_SPEC-085) or TLS-first, TLS-PSK, CA-verified and mutual TLS, certificate validation callbacks, custom SSLContext; TLS 1.2 minimum
  • v2 messages: Direct PDU (several commands per request) and Read Audit Log
  • Command parsers: 42 commands in the registry (parse_command), including extended commands above 253 via Command 31
  • Robust parsing: byte count and checksum validation, delayed-response retries, communication-error decoding
  • Lookup tables: 178 engineering unit codes, 551 manufacturer IDs, status and classification decoders
  • High-level API: Device caches identity (Commands 0/13/20) and reads process values on access
  • Multidrop: address each device on a loop by poll address or unique address
  • Declarative parsing: wire structs via the construct library
  • Typed: ships py.typed

Requirements

  • Python 3.10+
  • Python 3.13+ for TLS-PSK (ssl PSK callbacks)
  • construct>=2.10
  • No root privileges: HART-IP is plain UDP/TCP

Installation

pip install hartip-py

Runnable scripts live in examples/.

Quick start

from hartip import HARTIPClient, parse_cmd0, parse_cmd1

# v1 over UDP (default)
with HARTIPClient("192.168.1.100") as client:
    resp = client.read_unique_id()
    info = parse_cmd0(resp.payload)
    print(info.manufacturer_name, info.device_id)

    resp = client.read_primary_variable()
    pv = parse_cmd1(resp.payload)
    print(f"{pv.value} (unit {pv.unit_code})")

TLS (HART-IP v2)

from hartip import HARTIPClient, HARTIP_V2_PSK_CIPHERS

# PSK authentication
with HARTIPClient(
    "192.168.1.100",
    protocol="tcp",
    version=2,
    psk_identity="client1",
    psk_key=b"\x00" * 16,
    ciphers=HARTIP_V2_PSK_CIPHERS,
) as client:
    resp = client.read_unique_id()

# CA-verified + mutual TLS
with HARTIPClient(
    "192.168.1.100",
    protocol="tcp",
    version=2,
    ca_certs="/path/to/ca.pem",
    certfile="/path/to/client.pem",
    keyfile="/path/to/client.key",
) as client:
    resp = client.read_unique_id()

# Custom certificate validation callback
def pin_cert(cert_dict):
    subject = dict(x[0] for x in cert_dict.get("subject", ()))
    return subject.get("commonName") == "my-device.local"

with HARTIPClient(
    "192.168.1.100",
    protocol="tcp",
    version=2,
    cert_validator=pin_cert,
) as client:
    resp = client.read_unique_id()

# Full custom SSLContext
import ssl
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.load_verify_locations("/path/to/ca.pem")

with HARTIPClient("192.168.1.100", protocol="tcp", version=2, ssl_context=ctx) as client:
    resp = client.read_unique_id()

Publish (burst) frames

Devices in burst mode, and gateways with an active subscription, send unsolicited Publish frames on the client's session. The client skips them while it waits for the reply to a request. To receive them, pass a callback:

from hartip import HARTIPClient

def on_publish(resp):
    print(resp.command_number, resp.parsed)

with HARTIPClient("192.168.1.100", protocol="tcp", on_publish=on_publish) as client:
    client.read_primary_variable()

Publish frames are only read while a request is outstanding; there is no background reader. Frames that fail validation are dropped.

Error handling

from hartip import HARTIPClient
from hartip.exceptions import (
    HARTIPConnectionError,
    HARTIPTimeoutError,
    HARTIPTLSError,
    HARTResponseError,
)

with HARTIPClient("192.168.1.100") as client:
    resp = client.read_unique_id()

    # Check manually
    if not resp.success:
        print(resp.error_message)

    # Or raise
    resp.raise_for_error()  # raises HARTResponseError on failure

Exception hierarchy:

HARTError
├── HARTIPError
│   ├── HARTIPTimeoutError
│   ├── HARTIPConnectionError
│   │   └── HARTIPTLSError
│   └── HARTIPStatusError
└── HARTProtocolError
    ├── HARTChecksumError
    ├── HARTFrameError (also a ValueError)
    ├── HARTResponseError
    └── HARTCommunicationError

Convenience methods

Method HART Command
read_unique_id() 0
read_primary_variable() 1
read_current_and_percent() 2
read_dynamic_variables() 3
read_tag_descriptor_date() 13
read_output_info() 15
read_long_tag() 20
read_additional_status() 48
send_command(cmd, ...) any
send_direct_pdu(commands) v2 Direct PDU
read_audit_log() v2 Audit Log

Commands above 253 go out wrapped in Command 31. The reply is unwrapped for you: command_number reports the expanded number (e.g. 534), payload no longer starts with its 2 bytes, parsed uses the parser for 534, and pdu.command stays 31. A reply that carries a different expanded number raises HARTFrameError.

Testing

# Unit tests (no server needed)
pytest

# Integration tests (requires HART-IP server)
pytest -m integration

Support

If you find this project useful, consider supporting development:

Ko-fi

License

Dual-licensed under GPL-3.0 and a commercial license.

Free for open source use under GPL-3.0. If you want to use hartip-py in proprietary products without GPL obligations, a commercial license is available — see LICENSE-COMMERCIAL.md for details.

References

Trademarks

All marks belong to their owners; used nominatively to describe interoperability; no endorsement implied.

Metadata

Release files for hartip-py 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for hartip-py 0.4.0
File Size Uploaded Provenance Actions
hartip_py-0.4.0.tar.gz 130.4 kB With provenance Details for hartip_py-0.4.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for hartip-py 0.4.0
File Interpreter ABI Platform Actions
hartip_py-0.4.0-py3-none-any.whl Python 3 none any Details for hartip_py-0.4.0-py3-none-any.whl

Total release size: 208.6 kB

Release files / hartip_py-0.4.0.tar.gz

Download URL hartip_py-0.4.0.tar.gz
Size 130.4 kB
Tags Source
SHA-256 checksum
How to use checksums
86268aa0bafc824d57a73f46561232345b5a8709154ab0fcfdb27c505206444c
BLAKE2b-256 checksum
How to use checksums
1edf8c6eb39db917fc6795a0e4d9f4410108b03bed7290072845bc342583b352
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / hartip_py-0.4.0-py3-none-any.whl

Download URL hartip_py-0.4.0-py3-none-any.whl
Size 78.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7954619d095e8bf44d6ba2b1611fd14f0de21e8cb732a3159cb8fc6a02086ce8
BLAKE2b-256 checksum
How to use checksums
c5648f73cfa86dd17aea13d5de6c1fd450a60ca5275588a3c946e86144311242
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Security for hartip-py 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for hartip_py-0.4.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page