Metadata-Version: 2.4
Name: peha-phc-stmv3
Version: 0.1.1
Summary: A Python library to interact with PEHA PHC stmv3 controllers.
Author: Philipp Harms
License-Expression: MIT
Project-URL: Homepage, https://git.supercookie.de/philipp/peha-phc-stmv3
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests
Dynamic: license-file

# PEHA PHC STMv3 Python Library

A Python library for controlling and monitoring PEHA PHC System (STMv3) smart home devices.

## Features

- **Device Discovery:** automatically downloads and parses the project file (`project.ppfx`) from the STM controller to identify all connected modules (Inputs, Outputs, Dimmers, Shutters, Analog).
- **Control:** Switch Output, dimmers, and simulate inputs.
- **Monitoring:** Read output status, and read/write "Merker" (flag) values.
- **Input Simulation:** Simulate physical button presses (`simInputEvent`) to trigger internal logic or scenes.
- **Robust Catalog:** uses a reverse-engineered device catalog (`modules.xml`) to correctly map physical addresses for 50+ module types.

> **Note on live input/sensor state:** the STM's RPC surface has no way to read a physical input
> or sensor's live state directly, and no push/event mechanism either (see Limitations below). The
> supported pattern is: in the PHC Systemsoftware, wire the input/sensor to a "Merker" via basic
> programming (one-time setup per sensor), then poll that Merker with this library.

## Installation

```bash
pip install .
```

## Usage

### Connecting and Discovery

```python
from peha.client import PehaPHC

# Connect to the STM controller
client = PehaPHC("192.168.1.10")

# Get device info
info = client.who_are_you()
print(f"Connected to STM at address {info.get('STM-Address', 'Unknown')}")

# Discover modules
modules = client.get_modules()
for mod in modules:
    print(f"{mod['name']} - Addr: {mod['module_addr']} - {mod['description']}")
```

### Controlling a Light (Output)

```python
# Switch ON channel 0 of module at address 65
# (Note: Address 65 usually corresponds to the first output module)
client.switch_channel(stm_address=2, module_address=65, channel_index=0, state=True)
```

### Simulating a Button Press (Input)

Useful for triggering scenes or logic that depends on a physical switch.

```python
from peha.client import VirtualInputEvent

# Simulate a short press ON event on Input Module 0, Channel 0
client.sim_input_event(
    stm_addr=2, 
    module_addr=0, 
    channel=0, 
    event=VirtualInputEvent.ON_SHORT
)
```

### Reading/Writing a Merker (Flag)

Merker are project-wide boolean flags created in the PHC Systemsoftware. Since the STM has no way
to report a physical input's or sensor's live state directly (see Limitations), the supported
pattern for monitoring something like a motion sensor is: create a Merker, wire the sensor to it
via "basic programming" in the desktop software (once, per sensor), then poll the Merker here.

Merker are auto-discoverable, the same way modules are - `get_markers()` reads the same project
file `get_modules()` does (this is exactly how the PHC Systemsoftware itself finds and lists them
under the right STM):

```python
# Discover Merker (id + whatever you named them in the PHC Systemsoftware)
for marker in client.get_markers(stm_filter=2):
    print(marker['marker_id'], marker['description'])

# Read one
is_on = client.get_marker(stm_addr=2, marker_id=0)  # marker_type defaults to ANWENDERMERKER

# Write one (e.g. a flag your own logic reads elsewhere in the project)
client.set_marker(stm_addr=2, marker_id=0, state=True)
```

## Limitations

- **No live input/sensor monitoring via the STM's RPC surface.** There is no RPC call that
  returns a physical input's or sensor's current state, and no push/event mechanism for input
  changes either — confirmed by testing (a physical button press produced no change anywhere in
  the RPC surface) and by the fact that RPC methods that look like an event log
  (`checkInputs`/`checkOutputs`/`getEventBuffer`/`getEventCounter`) exist in the vendor client's
  code but are unused there and fault with "Feature not supported by device!" on real hardware.
  Use the Merker pattern above instead. Note that while Merker themselves are discoverable
  (`get_markers()`), *what feeds a given Merker* is not - the input→Merker wiring lives in the
  project's compiled logic binary, not the readable `project.ppfx`, so you'll only know a Merker's
  id and whatever name the user gave it, not which physical sensor (if any) actually drives it.
- **Output state on radio (EnOcean/Funk) actuators is optimistic, not verified.** `get_output_state()`
  reflects the last command *this library* sent, not a real hardware readback — these actuators
  only push a status telegram on an actual switching event, with no way to actively query current
  state. If the device is switched by other means (its own remote, a power cycle), the reported
  state silently goes stale. Wired (non-radio) output modules are expected to behave better, but
  this hasn't been hardware-verified either way yet.

## Advanced

### Address Logic
The library automatically calculates the correct physical address for modules based on their type:
- **Input Modules:** Offset `0` (e.g., Index 0 -> Addr 0)
- **Output Modules:** Offset `64` (e.g., Index 0 -> Addr 64)
- **Analog/RF Modules:** Offset `96` (e.g., Index 0 -> Addr 96)

### Device Catalog
The library includes a generated catalog (`peha/catalog_data.py`) derived from the official PEHA System Software `modules.xml`, ensuring accurate support for all module variants (e.g., `MFM_FUNK2`, `MCC_2`).
