A simulation-first Python SDK for building IoT and Cyber-Physical Systems. Develop and test your entire IoT system without physical hardware, then deploy to real devices with zero code changes.
- Simulation-First Development - Full system runs without hardware using realistic physical models
- Hardware Abstraction Layer - Swap between
mock,simulated, andhardwarebackends via configuration - Realistic Environment Models - Temperature, humidity, and soil moisture with proper physics (evaporation, daily cycles, etc.)
- Time Acceleration - Run simulations at 1x to 100x speed for rapid testing
- Pub/Sub Messaging - MQTT-style topic wildcards with local or distributed brokers
- Declarative Configuration - Define entire systems in YAML
- Web Dashboard - Real-time monitoring with React, charts, and simulation controls
- Fault Injection - Test resilience with sensor drift, stuck actuators, communication delays
- Hysteresis Control - Built-in controllers to prevent actuator oscillation
# Clone the repository
git clone https://github.com/yourusername/iot-component-sdk.git
cd iot-component-sdk
# Install in development mode
pip install -e ".[dev]"
# Install UI dependencies (optional, for dashboard)
cd ui && npm install && cd ..# Run the smart greenhouse example
iot-sdk run examples/configs/agriculture.yaml
# Access the dashboard at http://localhost:8080iot-sdk validate examples/configs/agriculture.yamliot-sdk list┌─────────────────────────────────────────────────────────────────┐
│ IoT System │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Sensor │ │ Sensor │ │Actuator │ │Controller│ │
│ │ (temp) │ │ (soil) │ │ (pump) │ │(hysteres)│ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │ │
│ └────────────┴────────────┴────────────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ │ Message Broker │ │
│ │ (Local or MQTT) │ │
│ └───────────┬───────────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ │ Simulation Engine │ │
│ │ - Time management │ │
│ │ - Environment models │ │
│ │ - Fault injection │ │
│ └───────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
| Concept | Description |
|---|---|
| Component | Building block: sensors, actuators, processors, controllers |
| Message | Unit of communication with type, payload, timestamp, and metadata |
| Broker | Routes messages between components (local in-memory or MQTT) |
| Simulation Engine | Manages time, environment models, and fault injection |
| HAL | Hardware Abstraction Layer with pluggable backends |
Each component supports multiple backends:
| Backend | Description | Use Case |
|---|---|---|
mock |
Returns fixed/random values | Unit testing |
simulated |
Uses physical models with noise/drift | Integration testing, demos |
hardware |
Connects to real GPIO/I2C/SPI | Production deployment |
Switch backends with a single config change:
components:
- id: temp-sensor
type: sensor.temperature
backend: simulated # Change to "hardware" for productionSystems are defined in YAML:
system:
name: "smart-greenhouse"
mode: "simulation"
simulation:
time_scale: 10.0 # 10x faster than real-time
tick_interval: 0.1
environment:
initial_hour: 8.0
num_zones: 1
broker:
type: local # or "mqtt" for distributed
api:
enabled: true
host: "0.0.0.0"
port: 8080
components:
# Temperature sensor publishing every 30 seconds
- id: temp-sensor-1
type: sensor.temperature
backend: simulated
config:
interval: 30
topic: sensors/temperature/zone1
# Soil moisture sensor
- id: soil-sensor-1
type: sensor.soil_moisture
backend: simulated
config:
interval: 30
topic: sensors/soil/zone1
zone: 0
# Water pump actuator
- id: pump-1
type: actuator.pump
backend: simulated
config:
command_topic: actuators/pump/command
state_topic: actuators/pump/state
flow_rate: 10.0
# Hysteresis controller for irrigation
# Turns pump ON when moisture < 30%, OFF when > 60%
- id: irrigation-controller
type: controller.hysteresis
config:
input_topic: sensors/soil/zone1
output_topic: actuators/pump/command
low_threshold: 30.0
high_threshold: 60.0
invert: falseConfiguration supports environment variable substitution:
broker:
type: mqtt
host: ${MQTT_HOST:localhost}
port: ${MQTT_PORT:1883}
username: ${MQTT_USER}
password: ${MQTT_PASS}| Type | Description | Key Config |
|---|---|---|
sensor.temperature |
Temperature readings (°C) | interval, topic |
sensor.humidity |
Relative humidity (%) | interval, topic |
sensor.soil_moisture |
Soil moisture (%) | interval, topic, zone |
| Type | Description | Key Config |
|---|---|---|
actuator.pump |
Water pump control | command_topic, state_topic, flow_rate |
actuator.relay |
Generic relay control | command_topic, state_topic |
| Type | Description | Key Config |
|---|---|---|
controller.hysteresis |
Bang-bang control with dead band | input_topic, output_topic, low_threshold, high_threshold |
controller.threshold |
Simple threshold alerts | input_topic, output_topic, threshold, comparison |
The simulation engine includes realistic physical models:
- Daily sine wave pattern (cooler at night, warmer at day)
- Configurable base temperature and amplitude
- Random noise for realism
- Inversely correlated with temperature
- Responds to rain events
- Bounded 0-100%
- Evaporation increases with temperature, decreases with humidity
- Irrigation adds moisture based on pump flow rate
- Drainage when oversaturated
- Per-zone tracking
class SoilMoistureModel:
def update(self, dt_hours: float, temperature: float, humidity: float) -> float:
# Evaporation rate depends on conditions
evap_rate = self.base_evaporation * (1 + 0.05 * (temperature - 20))
evap_rate *= (1 - humidity / 200)
# Irrigation adds moisture
if self.is_irrigating:
self.moisture += self.irrigation_rate * dt_hours
# Evaporation removes moisture
self.moisture -= evap_rate * dt_hours
return self.moistureThe SDK includes a React-based dashboard for real-time monitoring:
- System Overview - Component count, message throughput, uptime
- Real-time Charts - Sensor readings with Recharts
- Simulation Controls - Start/stop/pause, time acceleration slider
- Message Inspector - Filter and inspect pub/sub traffic
- Environment Panel - Current temperature, humidity, soil moisture
- Component List - Status and details for all components
The dashboard is served automatically when you run a system:
iot-sdk run config.yaml
# Dashboard available at http://localhost:8080To run the UI in development mode (hot reload):
# Terminal 1: Run the backend
cd /path/to/iot-component-sdk
python examples/run_demo_server.py
# Terminal 2: Run the frontend
cd ui
npm run dev
# Dashboard at http://localhost:5173Test system resilience with fault injection:
scenarios:
sensor-failure:
description: "Test behavior when soil sensor fails"
duration: 3600
events:
- time: 300
action: inject_fault
parameters:
component: soil-sensor-1
fault_type: sensor_stuck
- time: 600
action: clear_fault
parameters:
component: soil-sensor-1| Fault | Description |
|---|---|
sensor_stuck |
Sensor returns the same value repeatedly |
sensor_drift |
Readings drift from true value over time |
sensor_noise |
Excessive noise added to readings |
actuator_stuck_on |
Actuator ignores OFF commands |
actuator_stuck_off |
Actuator ignores ON commands |
communication_delay |
Messages are delayed by specified duration |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/system |
System status and configuration |
| GET | /api/components |
List all components |
| GET | /api/components/{id} |
Get specific component |
| GET | /api/simulation |
Simulation state |
| POST | /api/simulation/start |
Start simulation |
| POST | /api/simulation/stop |
Stop simulation |
| POST | /api/simulation/pause |
Pause simulation |
| POST | /api/simulation/resume |
Resume simulation |
| POST | /api/simulation/speed |
Set time scale {"time_scale": 10.0} |
| GET | /api/messages |
Recent message history |
| GET | /api/sensors/latest |
Latest sensor readings |
| GET | /api/environment |
Current environment state |
Connect to /api/ws for real-time updates:
const ws = new WebSocket('ws://localhost:8080/api/ws');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// data.type: "sensor_reading", "actuator_state", "system_event"
// data.payload: event-specific data
};# Run a system from configuration
iot-sdk run <config.yaml> [--no-api] [--port PORT]
# Validate a configuration file
iot-sdk validate <config.yaml>
# List available component types
iot-sdk list
# Show information about a component type
iot-sdk info <type>
# Example: iot-sdk info sensor.temperatureiot-component-sdk/
├── src/iot_sdk/
│ ├── core/
│ │ ├── component.py # Base Component class
│ │ ├── message.py # Message dataclass
│ │ ├── broker.py # Broker interface + LocalBroker
│ │ └── registry.py # Component registry
│ ├── components/
│ │ ├── sensors/ # Sensor implementations
│ │ ├── actuators/ # Actuator implementations
│ │ └── controllers/ # Controller implementations
│ ├── simulation/
│ │ ├── engine.py # SimulationEngine
│ │ ├── environment.py # Environment models
│ │ └── time.py # Time management
│ ├── api/
│ │ ├── app.py # FastAPI application
│ │ ├── routes.py # REST endpoints
│ │ ├── websocket.py # WebSocket handler
│ │ └── state.py # Application state
│ ├── system/
│ │ ├── factory.py # Component factory
│ │ ├── loader.py # YAML config loader
│ │ ├── builder.py # System builder
│ │ └── runner.py # System runner
│ └── cli.py # Command-line interface
├── ui/ # React dashboard
│ ├── src/
│ │ ├── components/ # React components
│ │ ├── hooks/ # Custom hooks
│ │ ├── pages/ # Page components
│ │ └── types/ # TypeScript types
│ └── package.json
├── examples/
│ └── configs/
│ └── agriculture.yaml # Smart greenhouse example
├── tests/
├── pyproject.toml
└── README.md
- Python 3.11+
- Node.js 18+ (for dashboard)
# Install Python dependencies
pip install -e ".[dev]"
# Install UI dependencies
cd ui && npm install
# Run tests
pytest
# Run tests with coverage
pytest --cov=iot_sdk
# Type checking
mypy src
# Linting
ruff check src tests
# Formatting
black src testsfrom iot_sdk.core.component import Component
from iot_sdk.core.message import Message
class CustomSensor(Component):
"""A custom sensor implementation."""
component_type = "sensor.custom"
def __init__(self, component_id: str, config: dict, broker):
super().__init__(component_id, config, broker)
self.interval = config.get("interval", 60)
self.topic = config.get("topic", f"sensors/custom/{component_id}")
async def start(self):
await super().start()
# Start reading loop
self._task = asyncio.create_task(self._read_loop())
async def _read_loop(self):
while self._running:
value = self._read_value()
message = Message(
type="sensor.reading",
source=self.id,
payload={"value": value, "unit": "custom"}
)
await self.publish(self.topic, message)
await asyncio.sleep(self.interval)
def _read_value(self) -> float:
# Your sensor reading logic here
return 42.0Register your component:
from iot_sdk.system.factory import get_factory
factory = get_factory()
factory.register("sensor.custom", CustomSensor)The primary example demonstrates automated irrigation:
- Soil moisture sensors monitor moisture levels in different zones
- Temperature/humidity sensors track environmental conditions
- Hysteresis controller makes irrigation decisions (ON below 30%, OFF above 60%)
- Water pump irrigates based on controller commands
- Dashboard provides real-time monitoring and manual override
- Rapid iteration - No hardware setup required
- Edge case testing - Simulate sensor failures, extreme conditions
- Time compression - Test days of operation in minutes
- Reproducibility - Same simulation produces same results
- Cost savings - Develop and test before purchasing hardware
- Team collaboration - Everyone can run the system locally
MIT License - see LICENSE for details.
Contributions are welcome! Please read CONTRIBUTING.md for guidelines.
- Built following NIST SP 800-183 IoT security guidelines
- Inspired by industrial IoT frameworks and digital twin concepts