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; parsers return TypedDicts (empty dict when the payload is too short)

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.5.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.5.0
File Size Uploaded Provenance Actions
hartip_py-0.5.0.tar.gz 142.5 kB With provenance Details for hartip_py-0.5.0.tar.gz

Built distribution (wheel)

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

Total release size: 227.9 kB

Release files / hartip_py-0.5.0.tar.gz

Download URL hartip_py-0.5.0.tar.gz
Size 142.5 kB
Tags Source
SHA-256 checksum
How to use checksums
a87ba61033919eaa0a0939d3546a61e57bce290282168ab381810e26774a87d0
BLAKE2b-256 checksum
How to use checksums
1035f9babda83be97e82c8f2c9591bfe14983800771070340c0c3cd0619b0da9
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.5.0-py3-none-any.whl

Download URL hartip_py-0.5.0-py3-none-any.whl
Size 85.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
876cc2c482427978a10f7d63e5be4c6fbee14f62b79c6e7ecc0263cc2cc2facf
BLAKE2b-256 checksum
How to use checksums
d6aa1139768b9c659cd73edcae6fc60bcf41567fd99f62cf95d9a00fc3b277f1
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.5.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.5.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