OpenSCADA Lite is a modular, extensible, and modern SCADA (Supervisory Control and Data Acquisition) + GIS platform written in Python.
Following the principle of keeping things simple, it is designed for rapid prototyping, research, and small-to-medium automation projects, with a focus on clarity, testability, and real-time feedback via WebSockets.
- Backend Modular architecture: Easily add new modules by extending the base module classes
- Driver abstraction: Plug in new drivers for different protocols or simulated devices.
- Real-time updates: Uses Flask-SocketIO for live data feeds to the frontend.
- React front end: Use the openscadalite.js to easily generate views for the backend modules.
- Event bus: Decoupled communication between modules.
- Type-safe DTOs: All messages use dataclasses for clarity and validation.
- Secure: All endpoints are secured automatically
- Configurable: All system structure is defined in JSON config files.
- Testable: Extensive unit and integration tests.
openscada_lite/
app.py # Main FastAPI app and SocketIO server
common/ # Common folder of shared resources by all modules
config/ # Configuration loader and validator
models/ # DTOs, entities, and event types
bus/ # Event bus implementation
tracking/ # Dataflow tracability utilities
utils/ # General utils
modules/ # Modules folder
alarm/ # Alarm life cycle management
alert/ # Alerting with popups for the frontend
animation/ # SVG animation generation
base/ # Base implementation for all modules (MSC pattern)
command/ # Command life cycle and feedback
communication/ # Driver management and communications
datapoint/ # Datapoint integrity and updates
frontend/ # Frontend tab configuration and dynamic UI
gis/ # Geospatial asset and icon management
rule/ # Automatic actions based on datapoint values
security/ # Login and endpoint security
stream/ # Video/data stream configuration and endpoints
tracking/ # Data flow tracking inside the SCADA system
loader.py # Module loader and registration
web/ # Frontend applications
config_editor/ # System configuration editor
login/ # Common login view
scada/ # SCADA application views
security_editor/ # Security configuration editor
config/ # Configuration files
logging_config.json # Loggin configuration
svg/ # SVG files for process graphics
system_config.json # Datapoint types, ranges, enums, system, rules...
security_config.json # Users and user groups
tests/ # Unit and integration tests
Dockerfile # Dockerfile configuration to run in a docker container
pip install -r requirements.txt$env:SCADA_CONFIG_PATH="config"
uvicorn openscada_lite.app:asgi_app --host 0.0.0.0 --port 5443The server will be ready on http://localhost:5443.
The Security Editor is a react web-based application that lets you manage users, groups, and permissions for your SCADA system through a simple web interface.
3.1.1. Open the Security Editor in your browser
Navigate to:
http://localhost:5443/security_editor
(or the URL provided by your deployment)
3.1.2. View and Edit Users & Groups
- The editor loads the current
security_config.json. - You’ll see a list of users and groups.
- Click a user or group to view or edit their permissions.
3.1.3. Add or Remove Users/Groups
- Use the “Add User” or “Add Group” buttons to create new entries.
- Fill in usernames, passwords (hashed or plain, depending on your setup), and assign groups.
3.1.4. Assign Permissions
- For each group, select which permissions (e.g.,
VIEW,CONTROL,ADMIN) they should have. - Assign users to groups for role-based access.
3.1.5. Save Changes
- Click “Save” to write your changes to
config/security_config.json. - The backend will reload the config and apply new permissions immediately.
3.1.6. Test Access
- Log in with a user account to verify permissions.
- Try accessing datapoints, commands, or views to confirm restrictions.
- Add a new operator user and assign them to the “operators” group.
- Give the “operators” group permission to view and control datapoints, but not to edit system configuration.
- Save and verify that the new user can log in and only see/control what you allowed.
The Config Editor is a react web-based tool for managing your SCADA system’s configuration files, such as system_config.json and SVG layouts.
It provides a user-friendly interface for editing datapoints, drivers, rules, and other system settings, making it easy to customize and extend your automation project.
3.2.1. Open the Config Editor in your browser
http://localhost:5443/config_editor
(or your deployed Railway/production URL)
3.2.2. View and Edit Configuration
- The editor loads the current
system_config.json. - You can browse and edit datapoints, drivers, enums, rules, and SVG mappings.
- Use the UI to add, remove, or modify entries.
3.2.3. SVG Layout Management
- Upload or edit SVG files for your system’s visual layout.
- Assign datapoints and animation types to SVG elements.
3.2.4. Save Changes
- Click “Save” to write your changes to
config/system_config.jsonand/or SVG files. - The backend will reload the config and apply changes immediately.
3.2.5. Test and Validate
- Use the live preview to check your configuration.
- The editor validates your changes and highlights errors before saving.
- Add a new driver and define its connection info.
- Create new datapoints and assign them to the driver.
- Set up rules for automatic actions or alarms.
- Upload an SVG layout and map datapoints to visual elements.
- Save and verify that your changes are reflected in the running SCADA system.
The SCADA Frontend is the main web interface for OpenSCADA Lite.
It provides real-time visualization, control, and monitoring of your automation system using modern React components and SVG graphics.
- Live Data Visualization: See real-time values for all datapoints, alarms, and system status.
- Interactive Controls: Send commands to devices and drivers directly from the UI.
- SVG-based Graphics: Visualize tanks, pumps, valves, and other equipment with dynamic animations.
- Alarm & Alert Display: View active alarms and receive pop-up alerts for critical events.
- User Authentication: Secure login and role-based access to views and controls.
- Responsive Design: Works on desktop and tablet browsers.
-
Dashboard:
Overview of system status, key metrics, and recent alarms.
Quick access to important controls and summary charts. -
Process Graphics:
Interactive SVG-based visualizations of your plant or process.
Clickable elements allow direct control (e.g., start/stop pumps, open/close valves). -
Alarms & Alerts:
List of active alarms and historical events.
Pop-up notifications for new or critical alarms. -
Datapoint Table:
Tabular view of all datapoints, showing live values, quality, and status.
Useful for diagnostics and detailed monitoring. -
Command Panel:
Interface for sending manual commands to devices or drivers.
Shows feedback and command status. -
Login:
Secure authentication for users.
Access to views and controls is based on user roles and permissions.
3.3.1. Open the SCADA frontend in your browser
http://localhost:5443/scada
(or your deployed Railway/production URL)
3.3.2. Log in
- Enter your username and password.
- Access is controlled by the security configuration.
3.3.3. Monitor and Control
- View live process graphics and dashboards.
- Click on interactive elements to send commands (e.g., start/stop pumps).
- Watch for alarms and alerts in the notification area.
- Log in as an operator.
- Monitor tank levels and pump status in real time.
- Click a valve to open/close it.
- Receive an alarm pop-up if a threshold is exceeded.
- Use the dashboard to track system performance.
OpenSCADA Lite uses a modular architecture based on the MSC pattern (Model, Service, Controller).
This design makes it easy to add new features, maintain code, and ensure clear separation of concerns.
-
BaseModel
Stores and manages the state of messages or entities (e.g., datapoints, alarms).
Provides methods for updating, retrieving, and listing stored objects. -
BaseService
Handles business logic, event bus communication, and message processing.
Receives messages from the event bus and controller, processes them, updates the model, and notifies the controller. -
BaseController
Manages frontend-backend communication via WebSocket and HTTP.
Publishes updates to clients, handles incoming requests, and validates data.
Each functional module (e.g., datapoint, alarm, command, security) extends the base MSC classes:
-
Model:
Inherit fromBaseModeland specify the type of message/entity it stores. -
Service:
Inherit fromBaseServiceand implement logic for handling messages, updating the model, and interacting with other modules. -
Controller:
Inherit fromBaseControllerand define endpoints, validation, and publish logic for the frontend.
Example: Datapoint Module
# Model
class DatapointModel(BaseModel[DatapointMsg]):
pass
# Service
class DatapointService(BaseService[TagUpdateMsg, DatapointCommand, DatapointMsg]):
def should_accept_update(self, msg: TagUpdateMsg) -> bool:
# Custom acceptance logic
return True
# Controller
class DatapointController(BaseController[DatapointMsg, DatapointCommand]):
def validate_request_data(self, data: DatapointCommand):
# Validate incoming command
return data You can see with almost no code your datapoint service is ready!
- Create Model, Service, and Controller classes in your module folder, inheriting from the base MSC classes.
- Define your DTOs (data transfer objects) for messages, commands, and events.
- Register your module in
app.pyby instantiating its controller and passing the model, service, and socketio as needed. - Implement custom logic in your service and controller as required.
- Consistency: All modules follow the same structure.
- Extensibility: Easily add new modules by extending the base classes.
- Testability: Each part (model, service, controller) can be tested independently.
- Separation of Concerns: Business logic, state management, and frontend communication are clearly separated.
Like we saw in the previous section, the server is composed of modules binded by the event bus. Each module has a specific purpose and is composed always of controller.py, model.py and service.py
Next we will describe the properties of the main modules
The communication module in OpenSCADA Lite manages all driver interactions, enabling connectivity to real or simulated devices.
It uses a flexible driver protocol, making it easy to add support for new hardware or protocols.
- Each driver implements the
DriverProtocolinterface (seedriver_protocol.py). - Drivers are managed by the
ConnectorManager, which handles driver lifecycle, subscriptions, and event routing. - Drivers publish tag updates, command feedback, and connection status via async callbacks.
5.1.2.1. Create a Driver Class
- Inherit from
DriverProtocol(seedriver_protocol.py). - Implement required methods:
connect,disconnect,subscribe,register_value_listener,register_communication_status_listener,register_command_feedback,send_command.
- Implement the simulation or hardware logic in your driver.
Example:
# my_new_driver.py
from openscada_lite.modules.communication.drivers.driver_protocol import DriverProtocol
class MyNewDriver(DriverProtocol):
async def connect(self): ...
async def disconnect(self): ...
def subscribe(self, datapoints): ...
def register_value_listener(self, callback): ...
async def register_communication_status_listener(self, callback): ...
def register_command_feedback(self, callback): ...
async def send_command(self, data): ...
@property
def server_name(self): ...
@property
def is_connected(self): ...5.1.2.2. Register Your Driver
- Add your driver class to the
DRIVER_REGISTRYdictionary in the communication module. - Example:
DRIVER_REGISTRY = { "TankTestDriver": TankTestDriver, "BoilerTestDriver": BoilerTestDriver, "TrainTestDriver": TrainTestDriver, "MyNewDriver": MyNewDriver, # <-- Add your driver here }
5.1.2.3. Configure Your Driver in the System Config
- Add a new entry to the
"drivers"section of yoursystem_config.json:{ "name": "MyDevice", "driver_class": "MyNewDriver", "connection_info": { "ip": "192.168.1.100", "port": 502 }, "datapoints": [ { "name": "TEMPERATURE", "type": "float" }, { "name": "PRESSURE", "type": "float" } ] }
5.1.2.4. Implement Simulation or Hardware Logic
- For simulated drivers, implement the
_simulate_valuesmethod to periodically update datapoint values. - For real hardware, implement communication logic in
send_command,connect, etc.
5.1.2.5. Test Your Driver
- Start the backend and verify your driver connects, publishes updates, and responds to commands.
- Use the SCADA frontend and Config Editor to monitor and control your new device.
- TankTestDriver: Simulates a tank with level, pump, and door.
- BoilerTestDriver: Simulates a boiler with valve, pressure, temperature, and heater.
- TrainTestDriver: Simulates a train controller (extend as needed).
See the drivers/test/ folder for reference implementations.
- Use async methods for all I/O and event publishing.
- Always register your driver in
DRIVER_REGISTRYand the config file. - Use the provided DTOs (
RawTagUpdateMsg,CommandFeedbackMsg,DriverConnectStatus) for communication. - Test with both simulated and real hardware for reliability.
The Rule Module in OpenSCADA Lite enables automatic actions and logic based on datapoint values, alarms, and system events.
It allows you to define rules for triggering commands, alarms, alerts, or other actions—including direct action commands—when specific conditions are met.
- Flexible Rule Engine: Define rules using expressions based on datapoint values.
- Automatic Actions: Trigger commands, alarms, alerts, or other actions when rule conditions are satisfied.
- Action Commands: Rules can directly send commands to devices or drivers, automating control logic.
- Modular Actions: Each action is implemented as a class and registered in the
ACTION_MAP, making it easy to add new types of rule actions. - Datapoint Monitoring: Rules can react to any datapoint update in the system.
- Extensible: Add new rule types or actions as needed.
- Rules are defined in the
system_config.jsonfile under the"rules"section. - Each rule specifies:
- Conditions: Expressions evaluated against current datapoint values.
- Actions: What to do when the condition is met (e.g., send a command, raise an alarm, trigger an alert).
- The rule engine monitors all relevant datapoints and evaluates rule conditions in real time.
A unique feature of the Rule Module is its modular action system.
Each action (such as sending a command, raising an alarm, or alerting a client) is implemented as a class derived from the abstract Action base class.
Actions are registered in the ACTION_MAP dictionary, allowing the rule engine to dynamically execute them by name.
Example: ACTION_MAP registration
ACTION_MAP = {
"send_command": SendCommandAction(),
"raise_alarm": RaiseAlarmAction(),
"lower_alarm": LowerAlarmAction(),
"client_alert": ClientAlertAction()
}Adding a new action:
To add a new type of rule action, simply create a new class inheriting from Action, implement the get_event_data method, and register it in ACTION_MAP.
{
"rules": [
{
"name": "HighTankLevelAlarm",
"on_condition": "WaterTank@TANK > 80",
"on_actions": ["raise_alarm('Tank level high!')"],
"off_actions": ["lower_alarm()"]
},
{
"name": "AutoPumpStart",
"on_condition": "WaterTank@TANK > 60 and WaterTank@PUMP == 'CLOSED'",
"on_actions": ["send_command('WaterTank@PUMP', 'OPEN')"]
},
{
"name": "ShowClientAlert",
"on_condition": "AuxServer@VALVE == 'CLOSED' and AuxServer@PRESSURE > 100",
"on_actions": ["client_alert('Pressure high!', 'warning', 'AuxServer@VALVE', 'TOGGLE', 10)"]
}
]
}- The
"send_command"action will send a command to the specified target datapoint with the given value when the condition is met. - The
"raise_alarm"and"lower_alarm"actions manage alarm lifecycle. - The
"client_alert"action sends a notification to the frontend, optionally with a command button.
-
Open
system_config.json
Locate the"rules"section. -
Define a new rule
- Set a unique
name. - Write an
on_conditionexpression using datapoint identifiers. - Specify the
on_actionsand/oroff_actionsas a list of action strings.
- Set a unique
-
Save and reload
- Save your changes.
- The backend will reload the config and apply new rules automatically.
- Add new action types by creating a new class in
modules/rule/actioncommands/, inheriting fromAction, and registering it inACTION_MAP. - Rules can be made more complex by combining multiple conditions or chaining actions.
- Use clear, descriptive rule names.
- Test rule conditions and action commands to avoid unintended triggers.
- Use the Config Editor for a user-friendly way to manage rules.
The Animation Module enables dynamic, real-time updates of SVG elements in the SCADA web interface. It supports animations triggered by datapoint changes, alarm events, and communication status, providing rich and informative visual feedback.
-
SVG Mapping:
SVG elements are annotated with attributes such asdata-datapoint,data-animation, and optionallycommand-datapointfor interactive controls. -
Animation Configuration:
Animation types and behaviors are defined inanimation_config.json, mapping triggers to attribute changes, text updates, and durations. -
Handlers:
Specialized handlers process different types of backend messages:TagHandler: Handles datapoint value changes.AlarmHandler: Handles alarm lifecycle events.ConnectionHandler: Handles driver communication status.
-
Live Updates:
When a relevant event occurs, the backend uses the appropriate handler to process the message and generate anAnimationUpdateMsg.
This message contains the animation configuration (attributes, text, duration) and is sent to subscribed clients.
- The backend listens for
TagUpdateMsg(and other relevant messages) on the internal bus. - When an update arrives:
- The backend uses
animation_config.jsonto compute the GSAP config (attributes, text, duration). - It sends an
AnimationUpdateMsgto subscribed clients.
- The backend uses
- The HTML page loads the selected SVG and subscribes to
AnimationUpdateMsg. - GSAP applies animations to target elements:
gsap.to(elem, {
duration: msg.config.duration,
attr: msg.config.attr, // optional
text: msg.config.text // optional, requires TextPlugin
});- Command-capable elements (
command-datapoint) can send control messages back to the server when clicked.
Define SVG Elements:
Each interactive or animated element must include data-datapoint and data-animation.
<circle id="pump"
cx="70" cy="200" r="20"
fill="gray"
data-datapoint="WaterTank@PUMP"
data-animation="toggle_start_stop"
command-datapoint="WaterTank@PUMP_CMD"
command-value="TOGGLE" />Define Animation Types:
Animation behaviors are defined in animation_config.json.
Supported types:
- height_y → animates height and y attributes (e.g., tank level)
- fill_color → animates fill color based on numeric values (e.g., temperature)
- fill_toggle → changes color based on enum/string/boolean values (e.g., STARTED/STOPPED)
- text → animates text content
Example toggle mapping:
"toggle_start_stop": {
"type": "fill_toggle",
"map": {
"STARTED": "green",
"STOPPED": "gray"
},
"duration": 0.3
}- Update
animation_config.json:
"valve_toggle": {
"type": "fill_toggle",
"map": {
"OPENED": "green",
"CLOSED": "red"
},
"duration": 0.3
}- Update SVG:
<rect id="valve"
x="180" y="360" width="40" height="20"
fill="gray"
data-datapoint="AuxServer@VALVE"
data-animation="valve_toggle"
data-command="AuxServer@VALVE_CMD" />No frontend code changes required — the AnimationService handles mapping and emits AnimationUpdateMsg.
The GSAP client automatically renders the animation.
Handlers are responsible for processing messages and updating SVG elements:
- TagHandler:
Triggers on datapoint updates. Applies attribute or text changes based on value and quality. - AlarmHandler:
Triggers on alarm events. Determines alarm state (ACTIVE,ACK,INACTIVE,FINISHED) and updates mapped SVG elements. - ConnectionHandler:
Triggers on driver connection status. Updates SVG elements to reflect communication health.
Each handler uses the shared animation configuration and can schedule automatic reverts using the revert_after property.
To add a new handler:
-
Create a Handler Class:
Inherit from a base handler or follow the pattern inhandlers/. Implementcan_handle(msg)andhandle(msg, service)methods.class CustomHandler: def can_handle(self, msg) -> bool: return isinstance(msg, CustomMsgType) def handle(self, msg, service): # Process message and return list of AnimationUpdateMsg ...
-
Register the Handler:
Add your handler to thehandlerslist inAnimationService.self.handlers = [ TagHandler(), AlarmHandler(), ConnectionHandler(), CustomHandler(), # <-- Add your new handler here ]
-
Update Animation Config:
Define new animation types or triggers inanimation_config.jsonas needed.
- SVG elements declare datapoints and animation types.
- Handlers process backend events and broadcast
AnimationUpdateMsgto clients. - Frontend applies animations using GSAP.
- Architecture is scalable, flexible, and easily extensible for new event types and handlers.
The Alarm Module manages the lifecycle of alarms within the SCADA system, including raising, acknowledging, and lowering alarms. It ensures that alarm states are tracked, validated, and broadcast to other modules and the frontend.
-
AlarmModel:
Stores and updates alarm messages. Automatically removes finished alarms (deactivated and acknowledged) from the store. -
AlarmController:
Handles incoming requests to acknowledge alarms. Validates requests to ensure the alarm exists, is not finished, and has not already been acknowledged. -
AlarmService:
Processes messages to raise or lower alarms, updates the model, and publishes alarm updates to the event bus. Handles controller messages for acknowledgments and ensures proper state transitions. -
Utils:
Provides helper functions, such as retrieving the latest alarm for a given rule.
-
Raise Alarm:
Creates a new alarm or resets deactivation and acknowledgment times for an existing alarm. -
Acknowledge Alarm:
Marks the alarm as acknowledged if it is active and not already acknowledged. -
Lower Alarm:
Sets the deactivation time for the alarm. -
Finish Alarm:
An alarm is considered finished when both deactivation and acknowledgment times are set. Finished alarms are removed from the store.
To add new alarm behaviors or integrate with other modules:
-
Extend the Model:
Add new fields or methods toAlarmModelas needed. -
Customize the Controller:
Override validation or request handling logic inAlarmController. -
Enhance the Service:
Implement new message types or processing logic inAlarmService.
Use the event bus to broadcast custom alarm events. -
Add Utilities:
Place reusable logic inUtilsfor easier maintenance.
- Centralized alarm management with clear lifecycle handling.
- Validation and state transitions are enforced by the controller and service.
- Extensible architecture for custom alarm logic and integrations.
- Alarm updates are published to the event bus for system-wide visibility.
The Command Module manages the sending and feedback of control commands within the SCADA system. It provides a secure and structured way for clients to issue commands to devices and receive execution feedback.
-
CommandModel:
Maintains the set of allowed commands and stores feedback for each command. Initializes feedback entries for all permitted command identifiers. -
CommandController:
Handles incoming command requests from clients. Validates request data and forwards commands to the backend for execution. -
CommandService:
Processes command messages, updates the model with feedback, and publishes command feedback to the event bus. Accepts all feedback updates from command executors by default.
-
Send Command:
Clients send aSendCommandMsgspecifying the target datapoint and desired value. -
Validate and Forward:
The controller validates the request and forwards it to the backend. -
Execute and Feedback:
The backend executes the command and generates aCommandFeedbackMsgcontaining the result and any feedback. -
Notify Clients:
The service updates the model and notifies subscribed clients with the feedback message.
To add new command types or customize command handling:
-
Extend the Model:
Add new fields or logic toCommandModelfor custom feedback or command tracking. -
Customize the Controller:
Overridevalidate_request_datainCommandControllerto enforce additional validation rules. -
Enhance the Service:
Implement custom acceptance logic inshould_accept_updateor add new processing steps inCommandService.
- Centralized command management and feedback.
- Secure validation and execution workflow.
- Extensible architecture for custom command logic and integrations.
- Feedback is published to the event bus for system-wide visibility.
The Datapoint Module manages the acquisition, validation, and distribution of real-time process values (tags) within the SCADA system. It ensures that only valid and up-to-date datapoint updates are accepted and broadcast to other modules and clients.
-
DatapointModel:
Stores the current state of all allowed datapoints asTagUpdateMsgobjects. Initializes all tags with default values and tracks updates. -
DatapointController:
Handles incoming requests to update datapoints. Validates request data for required fields and correct format before passing to the model. -
DatapointService:
Processes raw tag update messages, validates them usingUtils.is_valid, and publishes accepted updates to the event bus. Converts raw messages to structuredTagUpdateMsgobjects. -
Utils:
Provides helper functions for validation, such as checking allowed tags and timestamp ordering to prevent outdated updates.
-
Receive Update:
The backend receives aRawTagUpdateMsgfrom a driver or client. -
Validate:
The controller checks for required fields and correct format.
The service usesUtils.is_validto ensure the tag is allowed and the timestamp is not older than the current value. -
Process and Broadcast:
Valid updates are converted toTagUpdateMsgand published to the event bus for system-wide visibility.
To add new datapoint behaviors or customize validation:
-
Extend the Model:
Add new fields or logic toDatapointModelfor custom tracking or initialization. -
Customize the Controller:
Overridevalidate_request_datainDatapointControllerto enforce additional validation rules. -
Enhance the Service:
Implement custom acceptance logic inshould_accept_updateor add new processing steps inDatapointService. -
Add Utilities:
Place reusable validation or processing logic inUtilsfor easier maintenance.
- Centralized management and validation of process values.
- Ensures only valid and up-to-date datapoint updates are accepted.
- Extensible architecture for custom datapoint logic and integrations.
- Updates are published to the event bus for system-wide visibility.
The Security Module provides authentication and authorization for the SCADA system. It manages user credentials, group permissions, and access control for API endpoints using JWT-based authentication.
-
SecurityModel:
Stores users and groups loaded from the configuration file. Maintains an in-memory copy and provides access to endpoint and permission data. -
SecurityController:
Exposes REST API endpoints for login, configuration management, and endpoint listing. Handles JWT token issuance and validation. -
SecurityService:
Implements user authentication, password hashing, and permission checks. Issues JWT tokens for authenticated users and verifies access rights for endpoints. -
Utils:
Provides helper functions for password hashing and JWT creation/verification.
-
Login:
Clients send credentials to/security/login. If valid, a JWT token is returned for session authentication. -
Authorization:
Protected endpoints require a valid JWT token. The controller verifies the token and checks user permissions before granting access. -
Configuration Management:
Security configuration (users, groups, permissions) can be retrieved and updated via the/security/api/configendpoints.
Unlike other modules, the Security Module does not listen to the internal event bus or publish messages.
It operates independently, providing synchronous REST API endpoints for authentication and authorization.
This design ensures that security logic is isolated and does not follow the base event-driven pattern used by datapoint, alarm, and command modules.
- Centralized authentication and authorization using JWT.
- REST API endpoints for login and configuration management.
- No event bus integration; operates independently from the base module pattern.
- Extensible for custom authentication logic and permission models.
The Tracking Module provides automatic tracking of data flow events throughout the SCADA system. It records the status and movement of DTOs (data transfer objects) for auditing, debugging, and monitoring purposes.
-
TrackingModel:
Stores the most recent data flow events (up to a configurable limit) using an ordered dictionary for efficient rotation. -
TrackingController:
Exposes a read-only API for retrieving tracking events. Does not accept incoming requests for updates. -
TrackingService:
Accepts all incoming tracking events and updates the model. Publishes events to the event bus for system-wide visibility. -
TrackingPublisher:
Handles publishing of tracking events. Uses a background worker thread to enqueue and process events, publishing to the event bus and optionally writing to a log file.
-
Event Generation:
Tracking events are generated whenever a decorated function is called or a DTO is processed.
Events include metadata such as source, status, timestamp, and payload. -
Event Publishing:
The publisher enqueues events for background processing.
Events are published to the event bus and optionally logged to a file. -
Event Retrieval:
Clients can query the tracking API to retrieve recent data flow events for auditing or debugging.
To automatically generate tracking information, use the provided decorators in common/tracking/decorators.py.
These decorators can be applied to methods to publish tracking events based on arguments or return values.
Examples:
-
Async function, DTO as first argument:
from openscada_lite.common.tracking.decorators import publish_from_arg_async from openscada_lite.common.tracking.tracking_types import DataFlowStatus @publish_from_arg_async(DataFlowStatus.RECEIVED) async def process_dto(self, dto): ...
-
Sync function, DTO as return value:
from openscada_lite.common.tracking.decorators import publish_from_return_sync from openscada_lite.common.tracking.tracking_types import DataFlowStatus @publish_from_return_sync(DataFlowStatus.SUCCESS) def handle_result(self, ...): ...
-
Decorator options:
publish_from_arg_async/publish_from_arg_syncpublish_from_return_async/publish_from_return_sync- Specify
statusand optionallysourcefor each event.
These decorators ensure that tracking events are published automatically whenever the decorated function is called, reducing manual tracking code and improving consistency.
- Centralized tracking of data flow events for auditing and debugging.
- Automatic event generation using decorators for async and sync functions.
- Efficient background publishing and optional file logging.
- Read-only API for retrieving recent tracking events.
The GIS module in OpenSCADA Lite provides geospatial visualization and management of assets, alarms, and datapoints on a map.
It integrates with the system configuration to display icons, states, and navigation links for each asset, and updates in real time as datapoint or alarm values change.
- GIS icons and their properties (location, icon, label, states, alarms) are defined in
system_config.jsonundergis_icons. - The GIS module listens for updates to relevant datapoints and alarms, updating icon states and visuals accordingly.
- The frontend can fetch GIS configuration via the
/api/gis/configendpoint.
- Edit the
gis_iconsarray in yoursystem_config.jsonto add new assets or update existing ones. - Each icon can have:
id: Unique identifiericon: Default icon pathlabel: Display namelatitude,longitude: Map coordinatesstates: Mapping of datapoint values to icon pathsalarm: Mapping of alarm states to icon pathsnavigation: Optional link or path for navigationrule_id: Associated alarm rule
{
"id": "camera1",
"icon": "/static/icons/camera.png",
"label": "Camera 1",
"latitude": 47.4328,
"longitude": 8.2384,
"datapoint": "CameraDriver@CAMERA_1_POSITION",
"states": {
"LEFT": "/static/icons/camera_left.png",
"RIGHT": "/static/icons/camera_right.png"
},
"alarm": {
"ACTIVE": "/static/icons/camera_ko.png"
},
"navigation": "stream/feed1",
"navigation_type": "popup",
"rule_id": "camera_1_alarm"
}-
Model:
GisModelstores the current state of all GIS icons and their properties. -
Service:
GisServiceprocesses incoming datapoint and alarm updates, determines icon state, and updates the model. -
Controller:
GisControllerexposes HTTP endpoints (e.g.,/api/gis/config) and manages frontend-backend communication.
- Configure GIS icons in
system_config.json. - The frontend map view fetches icon configuration and displays assets at their coordinates.
- Datapoint states and alarms update in real time as the system runs.
- Add a new GIS icon for a camera or camera in
system_config.json. - Configure a stream camerat to display for viewing in the right view or use the navigation_type="popup" to open the camera stream in a floating window
- Assign a datapoint and alarm rule to the icon.
- Save and reload the system.
- View the asset on the map; its icon and state update automatically as values change.
The stream module in OpenSCADA Lite manages video and data streams for integration with cameras, sensors, and other real-time sources.
It allows you to define streams in your system configuration and exposes them to the frontend for visualization and control.
- Streams are defined in the
streamsarray of yoursystem_config.json. - Each stream entry includes properties such as
id,description,server,port,protocol, andpath. - The backend exposes a
/streamsendpoint to list all configured streams. - The frontend can use this endpoint to display available streams and connect to them.
{
"id": "feed1",
"description": "Feed 1 - 1080p",
"server": "www.openscadalite.com",
"port": "8444",
"protocol": "https",
"path": "feed1/feed1.m3u8"
}-
Model:
StreamModel(inherits fromBaseModel) — stores stream configuration and state. -
Service:
StreamService(inherits fromBaseService) — handles business logic for stream updates (currently minimal). -
Controller:
StreamController(inherits fromBaseController) — exposes the/streamsendpoint to return the list of streams from config.
- Add or edit stream entries in
system_config.jsonunder thestreamsarray. - The frontend fetches the list of streams from
/streamsand displays them for user selection or monitoring. - Integrate with video players or data consumers in the frontend as needed.
- Add a new camera stream to
system_config.json. - Save and reload the system.
- The frontend lists the new stream and allows users to view or interact with it.
The frontend module in OpenSCADA Lite manages the configuration of tabs and views for the main SCADA web interface.
It allows you to define which tabs are available in the frontend, making the UI structure dynamic and easily configurable.
- Tabs are defined in the
frontendmodule entry in yoursystem_config.jsonunder themodulesarray. - The backend exposes a
/frontend/api/tabsendpoint that returns the list of configured tabs. - The frontend fetches this list and renders the corresponding tab buttons and views dynamically.
{
"name": "frontend",
"config": {
"tabs": [
"Main",
"Image",
"Datapoints",
"Communications",
"Alarms",
"Commands",
"Tracking",
"Streams"
]
}
}-
Model:
FrontendModel(inherits fromBaseModel) — stores frontend configuration state. -
Service:
FrontendService(inherits fromBaseService) — currently minimal, reserved for future business logic. -
Controller:
FrontendController(inherits fromBaseController) — exposes the/frontend/api/tabsendpoint to return the list of tabs from config.
- Edit the
tabsarray in thefrontendmodule config insystem_config.jsonto add, remove, or reorder tabs. - The frontend automatically updates to reflect the new tab configuration after reload.
- Use the Config Editor to manage the frontend module config visually.
- Add a new tab name to the
tabsarray insystem_config.json. - Save and reload the system.
- The SCADA frontend displays the new tab and view automatically.
All SCADA frontend views leverage a common live feed library, useLiveFeed, which unifies real-time updates and command handling.
This makes adding new views for any backend MSC module fast, consistent, and minimal code.
- Automatic WebSocket connection to receive live updates from the backend.
- Reactive state management:
itemsalways reflect the latest data. - Unified command/REST interface:
postJson()sends commands or updates to the backend with proper headers. - Flexible keying: Define a unique key per item for state mapping.
- Security: passes the necessary credentials to the backend
const [items, setItems, postJson] = useLiveFeed(
endpoint, // backend MSC module name
updateMsgType, // WebSocket message type
getKey, // function returning unique key for each item
postType? // optional: REST message type for sending updates/commands
);
items: object containing live data from the backend
setItems: optionally update items locally
postJson(payload): send a command or data update to the backend
Example: Alarms View
import React from "react";
import { useLiveFeed } from "../livefeed/useLiveFeed";
function alarmKey(a) { return a.alarm_occurrence_id; }
export default function AlarmsView() {
const [alarms] = useLiveFeed("alarm", "alarmupdatemsg", alarmKey);
return (
<div>
<h2>Active Alarms</h2>
<ul>
{Object.values(alarms).map(a => (
<li key={alarmKey(a)}>
{a.rule_id} — {a.datapoint_identifier}
</li>
))}
</ul>
</div>
);
}
Automatically subscribes to "alarm_alarmupdatemsg" and updates in real time.
with all headers handled automatically. Template for a New View
import React from "react";
import { useLiveFeed } from "../livefeed/useLiveFeed";
function myKey(item) { return item.id; }
export default function MyNewView() {
const [data, setData, postJson] = useLiveFeed(
"myendpoint",
"myupdatemsg",
myKey,
"mycommandmsg"
);
return (
<div>
<h2>My Endpoint Data</h2>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
Every view in the SCADA frontend alarms, datapoints, commands, GIS markers, communications, tracking, and animations—uses the same useLiveFeed hook.
OpenSCADA Lite exposes a FastAPI backend with automatic Swagger UI and OpenAPI schema generation. You can browse the interactive API docs at:
- Swagger UI: http://localhost:5443/docs
- OpenAPI JSON (served by FastAPI): http://localhost:5443/openapi.json
Additionally, this repository provides scripts to export the OpenAPI schema to a file and generate a TypeScript client library that can be used by the frontend to call all backend endpoints.
- Export the OpenAPI schema to openapi/openapi.json:
npm run openapi:export- Generate the TypeScript client to src/openscada_lite/web/lib/openApi/index.ts:
npm run openapi:client- Run both steps together:
npm run openapi:generateScript definitions are in package.json:
openapi:export→ runs the exporter at openapi/export_openapi.py to write openapi/openapi.jsonopenapi:client→ usesswagger-typescript-apito generate the client at src/openscada_lite/web/lib/openApi/index.ts
The generated client provides typed methods for all backend endpoints. Import it in your frontend code from src/openscada_lite/web/lib/openApi/index.ts and initialize it with your API base URL (e.g., http://localhost:5443). Once generated, you can call backend endpoints via the typed methods exposed by the client.
If you change or add backend routes, re-run npm run openapi:generate to refresh both the OpenAPI schema and the client.
Live Feed Data Flow
┌────────────────────────┐
│ Backend MSC Module │
│ │
│ ┌───────────────┐ │
│ │ WebSocket feed│─────┼─────────────┐
│ └───────────────┘ │ │
│ │ │
│ ┌───────────────┐ │ ▼
│ │ REST endpoint │─────► useLiveFeed
│ └───────────────┘ │ │
└────────────────────────┘ │
▼
┌─────────────────┐
│ React Component │
│ state (items) │
└─────────────────┘
│
User actions (button click / input)
│
▼
┌─────────────────┐
│ postJson() REST │
│ sends command │
└─────────────────┘
│
▼
Backend MSC handles command
🔹 New views can be added by defining endpoint, updateMsgType, getKey, and optionally postType. The live feed library handles WebSocket and REST automatically.
Run all tests with:
pytest -v tests/You can run specific tests or integration suites as needed.
All rights reserved to DFB Services LTD.
- Fork the repo and submit pull requests.
- Please add or update tests for any new features or bugfixes.
- For questions or suggestions, open an issue.
See the TODO file for upcoming features and ideas, including:
- Alarm and rule modules
- SVG/Canvas animations
- Feedback reasons and tracking IDs
- Logging service
- Raw-to-non-raw conversion
- Railway deployment
This project is licensed under the Apache License 2.0.
Keeping the demo server and OPC UA instance online has some running costs.
If you find OpenSCADA Lite useful and would like to support development or hosting, you can contribute here:
Buy Me a Coffee
GitHub Sponsors
PayPal
Your help keeps the demo server running and allows me to keep improving the project. Thank you!