Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.directenergypartners.com/llms.txt

Use this file to discover all available pages before exploring further.

The main entry point for your custom application is the app/main.py file inside the dep-ppl-app repository. This script handles the NATS connection to the PPL controller, sets up logging, and runs a continuous control loop where you implement your custom logic. You should edit this file directly to build your application. As your application grows, you can create additional modules and a full folder structure inside the app folder to organize your code.

The Main Script

Below is the default main.py script included in the repository:
app/main.py
"""
Main entry point for the app.
"""

import logging
import os
import sys
import time
from dotenv import load_dotenv

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from pplapp import Pplapp

# -- Configuration ------------------------------------------------------------
STARTUP_DELAY_S = 5
CONTROL_LOOP_INTERVAL_S = 5

# -- Logging ------------------------------------------------------------------
log = logging.getLogger("app")
log.setLevel(logging.INFO)
formatter = logging.Formatter(
    fmt="[%(asctime)s] %(levelname)s %(name)s %(message)s",
    datefmt="%d.%m.%Y %H:%M:%S",
)
_project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
fileHandler = logging.FileHandler(os.path.join(_project_root, "app.log"))
fileHandler.setFormatter(formatter)
log.addHandler(fileHandler)

consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(formatter)
log.addHandler(consoleHandler)

# -- Main Application Logic ---------------------------------------------------
def ems(app):
    # TODO: Implement energy management system logic
    pass

def main() -> None:
    load_dotenv()

    ipAddress = os.getenv("IP_ADDRESS")
    username = os.getenv("NATS_USERNAME")
    password = os.getenv("NATS_PASSWORD")

    if not ipAddress or not username or not password:
        log.error("IP_ADDRESS, NATS_USERNAME, and NATS_PASSWORD must be set in .env")
        sys.exit(1)

    log.info("Connecting to PPL controller at %s", ipAddress)
    app = Pplapp(ipAddress, username, password)

    time.sleep(STARTUP_DELAY_S)

    try:
        while True:
            try:
                ems(app)
            except Exception as e:
                log.exception("Error in control loop: %s", e)
            time.sleep(CONTROL_LOOP_INTERVAL_S)

    except KeyboardInterrupt:
        log.info("Shutdown requested")
        app.stop()
        log.info("Clean shutdown complete")


if __name__ == "__main__":
    main()

How It Works

The script does the following:
  • Loads environment variables from the .env file (IP_ADDRESS, NATS_USERNAME, NATS_PASSWORD).
  • Sets up logging to both the console and a file (app.log in the project root).
  • Connects to the controller using the pplapp module.
  • Runs a continuous control loop that calls the ems() function at a configurable interval.
  • Handles errors gracefully — exceptions in the loop are logged without crashing the application, and a keyboard interrupt triggers a clean shutdown.
The ems() function is where you implement your custom logic. The app parameter gives you access to all pplapp module functions for reading data from and sending commands to the controller.

Extending Your Application

The app folder is your workspace. As your application grows beyond a single file, you can create a full folder structure inside it. For example:
App Folder Structure
app/
├── __main__.py          # Allows running the app with python -m app
├── main.py              # Main entry point – edit this to implement your application
├── helpers/
│   ├── __init__.py
│   └── calculations.py
└── strategies/
    ├── __init__.py
    └── peak_shaving.py
Import your modules in main.py and call them from the ems() function. This keeps your code modular and maintainable.