Skip to main content

pyS7


pyS7 is a lightweight, pure Python library that implements the Siemens S7 communication protocol over ISO-on-TCP (RFC1006). It provides operations for S7-200, S7-300, S7-400, S7-1200, and S7-1500 PLC families; actual compatibility depends on CPU and PLC configuration.

Production/Stable • 2x Performance • Fully Tested

⚠️ Neither this project nor its authors are affiliated with Siemens. S7-200, S7-300, S7-400, S7-1200, and S7-1500 are registered trademarks of Siemens AG.

ha-s7plc banner

Features

  • Pure Python – No external dependencies, easy installation across platforms
  • Intuitive API – Clean, readable code with full typing support for IDE assistance
  • Async Support – Full asyncio client (AsyncS7Client) for non-blocking I/O
  • High Performance – Optimized hot paths, 2x faster request preparation (v2.5.0)
  • Graceful error handling – read_detailed() and write_detailed() provide per-tag success/failure info
  • Strict batch writes – Batch execution with structured failures and optional best-effort rollback
  • Optimized multi-variable reads – Automatically groups contiguous tags to reduce network requests
  • Automatic chunking – Transparently splits large STRING/WSTRING reads exceeding PDU size
  • Siemens TIME – DB TIME values represented by exact datetime.timedelta values
  • CPU diagnostics – Read PLC status (RUN/STOP) and information (model, firmware) via SZL protocol
  • Broad compatibility – Supports S7-200/300/400/1200/1500 series
  • Validated protocol core – Extensive automated tests and strict type checking

Safety Notice

Industrial safety must always remain your top priority. By using pyS7 you accept full responsibility for any damage, data loss, downtime, or unintended effects. Understand your system and the implications of each operation before interacting with live equipment.

Installation

Requires Python 3.11 or later.

Upgrading from 2.x? Read the 3.0 migration guide and the evidence-based compatibility matrix.

pip install pys7

Or install from GitHub:

pip install git+https://github.com/xtimmy86x/pyS7

Quick Start

Reading data

from pyS7 import BatchWriteError, S7Client

with S7Client(address="192.168.5.100", rack=0, slot=1) as client:
    tags = [
        "DB1,X0.0",     # Bit 0 of DB1
        "DB1,SINT20",   # SINT (signed byte -128 to 127) at byte 20 of DB1
        "DB1,USINT21",  # USINT (unsigned byte 0-255) at byte 21 of DB1
        "DB1,I30",      # INT at byte 30 of DB1
        "M54.4",        # Bit 4 of marker memory
        "IW22",         # WORD at byte 22 of input area
        "QR24",         # REAL at byte 24 of output area
        "DB1,S10.5"     # String of 5 characters at byte 10 of DB1
    ]

    data = client.read(tags)
    print(data)  # [True, -50, 200, 123, True, 10, 3.14, 'Hello']

Writing data

from pyS7 import S7Client

with S7Client(address="192.168.5.100", rack=0, slot=1) as client:
    tags = ["DB1,X0.0", "DB1,SINT20", "DB1,USINT21", "DB1,I30", "DB1,R40", "DB1,S10.5"]
    values = [True, -50, 200, 25000, 1.2345, "Hello"]

    client.write(tags, values)

Graceful error handling

from pyS7 import S7Client

with S7Client(address="192.168.5.100", rack=0, slot=1) as client:
    # read_detailed() continues on errors, returns per-tag results
    results = client.read_detailed(["DB1,I0", "DB99,I0", "DB1,R4"])

    for result in results:
        if result.success:
            print(f"{result.tag}: {result.value}")
        else:
            print(f"{result.tag} failed: {result.error}")

    # write_detailed() provides per-tag success/failure info
    tags = ["DB1,I0", "DB1,I2", "DB99,I0"]
    values = [100, 200, 300]
    write_results = client.write_detailed(tags, values)

    for result in write_results:
        if result.success:
            print(f"✓ {result.tag}: Written")
        else:
            print(f"✗ {result.tag}: {result.error}")

Strict batch writes with best-effort rollback

Batch writes are not atomic. A successful result means the PLC write response reported success; pyS7 does not perform post-write read-back verification. If any result fails, commit raises BatchWriteError. Optional rollback only attempts to restore a pre-write snapshot and can fail. PLC logic, concurrent clients, or a lost connection may observe or alter partially written values before restoration.

from pyS7 import BatchWriteError, S7Client

with S7Client(address="192.168.5.100", rack=0, slot=1) as client:
    # Batch write with best-effort rollback after a reported write failure
    with client.batch_write() as batch:
        batch.add("DB1,I0", 100)
        batch.add("DB1,I2", 200)
        batch.add("DB1,R4", 3.14)
        # Auto-commits on exit, rolls back on error

    # Or with explicit control
    batch = client.batch_write(auto_commit=False)
    batch.add("DB1,I0", 100)
    batch.add("DB1,I2", 200)

    try:
        results = batch.commit()  # Returns only when every write succeeded
    except BatchWriteError as exc:
        print(f"Write failed: {exc}")
        print(exc.rollback_attempted, exc.rollback_succeeded)

Reading CPU status

from pyS7 import S7Client

with S7Client(address="192.168.5.100", rack=0, slot=1) as client:
    # Get CPU status
    status = client.get_cpu_status()
    print(f"CPU Status: {status}")  # "RUN" or "STOP"

    # Get CPU information
    info = client.get_cpu_info()
    print(f"Model: {info['module_type_name']}")
    print(f"Firmware: {info['firmware_version']}")
    print(f"Hardware: {info['hardware_version']}")

See docs/CPU_STATUS_READING.md for details.

Async client

For asyncio-based applications (SCADA dashboards, IoT, Home Assistant, FastAPI):

import asyncio
from pyS7 import AsyncS7Client

async def main():
    async with AsyncS7Client(address="192.168.5.100", rack=0, slot=1) as client:
        # Read
        values = await client.read(["DB1,I0", "DB1,R4"])
        print(values)

        # Write
        await client.write(["DB1,I0"], [42])

        # Detailed read with per-tag error handling
        results = await client.read_detailed(["DB1,I0", "DB99,I0"])
        for r in results:
            print(f"{r.tag}: {r.value if r.success else r.error}")

        # Async batch write with rollback
        async with client.batch_write() as batch:
            batch.add("DB1,I0", 100)
            batch.add("DB1,I2", 200)

        # CPU diagnostics
        status = await client.get_cpu_status()
        print(f"CPU: {status}")

asyncio.run(main())

See docs/ADVANCED_USAGE.md for concurrent patterns and advanced usage.

String data types

pyS7 supports both ASCII and Unicode strings:

# STRING (ASCII) - All S7 models
tags = ["DB1,S10.20"]  # STRING at byte 10, max 20 chars
data = client.read(tags)
print(data[0])  # "Hello World"

# WSTRING (UTF-16-BE; PLC support/configuration dependent)
tags = ["DB1,WS100.30"]  # WSTRING at byte 100, max 30 chars
data = client.read(tags)
print(data[0])  # "Hello 世界! 🌍"

# Large strings automatically chunked if exceeding PDU size
tags = ["DB1,S100.254"]  # STRING[254] - handled transparently
data = client.read(tags)  # Complete string returned

Siemens TIME

from datetime import timedelta

value = client.read(["DB1,TIME100"])[0]  # returns timedelta
client.write(["DB1,TIME100"], [timedelta(seconds=1)])

TIME uses signed 32-bit big-endian milliseconds, with an inclusive range of −2,147,483,648 through 2,147,483,647 ms. Writes accept only timedelta values with exact millisecond precision; integers and sub-millisecond values are rejected rather than converted, rounded, or truncated. The 3.1.0 string-address contract is DB-only, using canonical syntax such as DB1,TIME100.

Monitoring and Metrics

Built-in metrics collection for monitoring PLC communication performance and diagnostics:

from pyS7 import S7Client

# Metrics enabled by default
client = S7Client(address="192.168.5.100", rack=0, slot=1)
client.connect()

# Perform operations
client.read(["DB1,I0", "DB1,R4"])
client.write(["DB1,I0"], [100])

# Access real-time metrics
print(f"Connected: {client.metrics.connected}")
print(f"Uptime: {client.metrics.connection_uptime:.1f}s")
print(f"Total operations: {client.metrics.total_operations}")
print(f"Success rate: {client.metrics.success_rate}%")
print(f"Error rate: {client.metrics.error_rate}%")
print(f"Avg read time: {client.metrics.avg_read_duration*1000:.1f}ms")
print(f"Throughput: {client.metrics.operations_per_minute:.1f} ops/min")

# Export to dict for logging/monitoring systems
metrics_dict = client.metrics.as_dict()

# Integration with Home Assistant, Prometheus, Grafana, etc.

See docs/METRICS.md for complete metrics documentation and integration examples.

Documentation

Guides

Technical Documentation

Examples

Example scripts in the examples/ directory demonstrate:

  • read_data.py – Basic reading operations
  • write_data.py – Basic writing operations
  • read_detailed_demo.py – Graceful error handling for reads
  • write_detailed_demo.py – Graceful error handling for writes
  • batch_write_demo.py – Strict batch writes with best-effort rollback
  • metrics_demo.py – Metrics collection and monitoring
  • get_cpu_status.py – CPU status monitoring
  • get_cpu_info.py – CPU information retrieval
  • read_data_tsap.py – TSAP connection example
  • bit_read_workaround.py – Bit operations
  • manage_reconnection.py – Connection handling
  • connection_state_demo.py – Connection state management
  • async_client_demo.py – Async client usage with asyncio

License

This project is distributed under the MIT License. See the LICENSE file for details.

Acknowledgements

Special thanks to filocara for the original project that inspired this work.

Metadata

Release files for pys7 3.1.1

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

Source distribution (sdist)

Source distribution for pys7 3.1.1
File Size Uploaded
pys7-3.1.1.tar.gz 99.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pys7 3.1.1
File Interpreter ABI Platform
pys7-3.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 162.8 kB

Release files / pys7-3.1.1.tar.gz

Download URL pys7-3.1.1.tar.gz
Size 99.6 kB
Tags Source
SHA-256 checksum
How to use checksums
4ffc25929cd4c51d88444c592cdc6a46cfbb2337c87a951a758b3bd0e56d4310
BLAKE2b-256 checksum
How to use checksums
f2e5739313ff5a3c54093c55b73d0e7ee1edbbc49ca4d26c7b6812a6422dee10
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.16

Release files / pys7-3.1.1-py3-none-any.whl

Download URL pys7-3.1.1-py3-none-any.whl
Size 63.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2544c0d87314447b467a246034a7e845303e7481c6f774542dedeac4d9a972e5
BLAKE2b-256 checksum
How to use checksums
a4242a7c2ddcbc31266e9442ad8b4d61c5c6b5f65cf30b7c9bdf85e6fcbe24c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.16

Release history Release notifications | RSS feed

This release

3.1.1 This release

2 release files

3.1.0

2 release files

3.0.0

2 release files

2.8.2

2 release files

2.8.1

2 release files

2.8.0

2 release files

2.7.0

2 release files

2.6.2

2 release files

2.6.1

2 release files

2.6.0

2 release files

2.5.0

2 release files

2.4.0

2 release files

2.3.0

2 release files

2.2.1

2 release files

2.2.0

2 release files

2.1.0

2 release files

2.0.0

2 release files

1.5.2

2 release files

1.5.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.2.0

2 release files

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