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.3.tar.gz (24.0 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.3-py3-none-any.whl (20.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: statequark-0.5.3.tar.gz
  • Upload date:
  • Size: 24.0 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.3.tar.gz
Algorithm Hash digest
SHA256 e90f5d0a557f143d2df874cb98689ac54cd6449f8093c5088f50bbd6020dbbf5
MD5 69c9823344bb5d6acbf456cbe7c35fa1
BLAKE2b-256 70399b6a85208a60060716b608b6eec0380ae841b0193f666b2b167c8220a845

See more details on using hashes here.

File details

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

File metadata

  • Download URL: statequark-0.5.3-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.3-py3-none-any.whl
Algorithm Hash digest
SHA256 c10ad85447425a16fd4fb77bf89d90955a806eb885db5a194ea35f212192192f
MD5 2ba72801cb701c60249f441e9967cd5c
BLAKE2b-256 eb022198cf0fa28a6863b10b74d4887d548523242167892cf1c2b2991b52e0ac

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