Skip to main content

Lightweight atomic state management for IoT devices and embedded systems

Project description

StateQuark

License: MIT Python 3.10+

Atomic state management for IoT and embedded systems. Inspired by Jotai.

Installation

pip install statequark

Quick Start

from statequark import quark, batch

# Basic state
temperature = quark(20.0)
print(temperature.value)  # 20.0
temperature.set(25.5)

# Derived state (auto-updates when dependencies change)
temp_f = quark(lambda get: get(temperature) * 9/5 + 32, deps=[temperature])
print(temp_f.value)  # 77.9

# Subscriptions (returns unsubscribe function)
unsub = temperature.subscribe(lambda q: print(f"Temp: {q.value}"))
temperature.set(30.0)  # prints: Temp: 30.0
unsub()  # stop listening

# Reset to initial value
temperature.reset()  # back to 20.0

# Batch updates (single notification)
with batch():
    sensor1.set(25.0)
    sensor2.set(60.0)

Utilities

Storage (Persistent State)

from statequark import quark_with_storage

# State survives device reboot
config = quark_with_storage("device_config", {"threshold": 25.0})
config.set({"threshold": 30.0})  # Saved to .statequark/device_config.json

Reducer (Action-based Updates)

from statequark import quark_with_reducer

def motor_reducer(state, action):
    match action["type"]:
        case "START": return {**state, "running": True}
        case "SET_SPEED": return {**state, "speed": action["value"]}
    return state

motor = quark_with_reducer({"running": False, "speed": 0}, motor_reducer)
motor.dispatch({"type": "START"})
motor.dispatch({"type": "SET_SPEED", "value": 100})

Select (Partial Subscription)

from statequark import quark, select

sensor = quark({"temp": 25.0, "humidity": 60})
temp = select(sensor, lambda d: d["temp"])  # Only triggers when temp changes
temp.subscribe(lambda q: print(f"Temp changed: {q.value}"))

Family (Dynamic Quarks)

from statequark import quark, quark_family

sensors = quark_family(lambda id: quark(0.0))
living_room = sensors("living_room")
bedroom = sensors("bedroom")
kitchen = sensors("kitchen")

Debounce / Throttle (Noise Filtering)

from statequark import debounce, throttle

# Debounce: wait 100ms of silence before updating
temp = debounce(0.0, 0.1)

# Throttle: max 1 update per second
display = throttle(0, 1.0)

History (Undo/Redo)

from statequark import history

setting = history(50)
setting.set(60)
setting.set(70)
setting.undo()  # 60
setting.undo()  # 50
setting.redo()  # 60

Validate (Value Validation)

from statequark import validate, in_range, clamp

# Raise error on invalid
temp = validate(25.0, in_range(0, 100))

# Auto-clamp on invalid
temp = validate(25.0, in_range(0, 100), clamp(0, 100))
temp.set(150)  # Clamped to 100

Middleware (Extensible Hooks)

from statequark import middleware, logger, persist

storage = {}
counter = middleware(0)
counter.use(logger("counter"))  # Log changes
counter.use(persist(storage, "count"))  # Save to dict
counter.set(42)  # [counter] 0 -> 42

Loadable (Async State)

from statequark import quark, loadable

data = quark(None)
state = loadable(data)
state.set_loading()   # state.value.state == "loading"
state.set_data(42)    # state.value.state == "hasData"
state.set_error(err)  # state.value.state == "hasError"

IoT Example

from statequark import quark, quark_with_storage, validate, in_range, clamp

# Persistent config
config = quark_with_storage("config", {"threshold": 30})

# Validated sensor with auto-clamp
soil = validate(65.0, in_range(0, 100), clamp(0, 100))

# Derived alert
alert = quark(
    lambda get: get(soil) < get(config)["threshold"],
    deps=[soil, config]
)

# Hardware control
alert.subscribe(lambda q: gpio.output(PUMP_PIN, q.value))

API Reference

Core

Function Description
quark(value) Create state
quark(fn, deps=[...]) Create derived state
batch() Batch updates context

Quark Methods

Method Description
.value Get current value
.set(v) Set value (sync)
await .set_async(v) Set value (async)
.reset() Reset to initial
unsub = .subscribe(fn) Subscribe, returns unsubscribe function
.cleanup() Release resources

Utilities

Create new quarks (pass initial value):

Function Description
quark_with_storage(key, default) Persistent state
quark_with_reducer(init, reducer) Action-based state
quark_family(factory) Dynamic quark creation
debounce(init, delay) Debounced updates
throttle(init, interval) Throttled updates
history(init, max_size) Undo/redo support
validate(init, validator, on_invalid) Value validation
middleware(init) Middleware pipeline

Wrap existing quarks (pass source quark):

Function Description
select(source, selector) Partial subscription (read-only)
loadable(source) Async state wrapper

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

statequark-0.5.1.tar.gz (23.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

statequark-0.5.1-py3-none-any.whl (20.9 kB view details)

Uploaded Python 3

File details

Details for the file statequark-0.5.1.tar.gz.

File metadata

  • Download URL: statequark-0.5.1.tar.gz
  • Upload date:
  • Size: 23.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for statequark-0.5.1.tar.gz
Algorithm Hash digest
SHA256 917827d191ef796740e7e9814d0b948a22dda6aefd436f44be31ba6ee335efae
MD5 97696d5cab0f0382ac4f311dbe54da7b
BLAKE2b-256 d1bf3ed10d1f7c014a0a01991a2a956381a288703cf15e091cd9aab1c155d6ab

See more details on using hashes here.

File details

Details for the file statequark-0.5.1-py3-none-any.whl.

File metadata

  • Download URL: statequark-0.5.1-py3-none-any.whl
  • Upload date:
  • Size: 20.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for statequark-0.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a0ed14913850651178853f386713dbddee784eb3b3f1b542dea7b8ba04026112
MD5 a56ef80895f72a64331e3af0885762c6
BLAKE2b-256 4cda684323824706909239501470dbb88957055cdaf425c8b18dd2bb6df056b3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page