Skip to content

Dynamic export control/solar curtailment of inverters for dynamic connections (CSIP-AUS/SEP2/IEEE 2030.5), fixed/zero export or negative feed-in

License

Notifications You must be signed in to change notification settings

longzheng/open-dynamic-export

Repository files navigation

Logo

About

This project aims to implement dynamic export control/solar curtailment of inverters using Node.js/TypeScript to satisfy

  • dynamic connection requirements (CSIP-AUS/SEP2/IEEE 2030.5) of various Australian energy distributors (DNSPs)
  • fixed/zero export limitations (e.g. 1.5kW export limit)
  • two-way tariffs (e.g.time based) export limitation
  • negative feed-in (e.g. Amber) export limitation
flowchart LR
    ODE(("open-dynamic-export"))
    I["Inverter(s)"]
    M["Site meter"]

    CSIP-AUS --> DC
    DC & F & T & NFI & MQTTL --> ODE
    Configuration --> F
    A["Amber API"] --> NFI
    R["Tariff rules"] --> T
    ODE <--> SunSpec & HTTP & Proprietary & MQTT
    Protocols <--> I & M

    subgraph Limiters
    DC["Dynamic exports"]
    F["Fixed/zero export"]
    T["Two-way tariffs"]
    NFI["Negative feed-in"]
    MQTTL["MQTT"]
    end

    subgraph Protocols
    SunSpec
    HTTP
    MQTT
    Proprietary
    end
Loading

Supported inverters and site meter

Inverters:

Name config.json inverter type Description Tested models
SunSpec sunspec SunSpec Modbus TCP compatible inverter
Must support models 1, 101/102/103, 120, 121, 122, 123
Fronius Primo
Fronius Symo

Site meter:

Name config.json meter type Description Tested models
SunSpec sunspec SunSpec Modbus TCP compatible smart meter
Must support models 1, 201/202/203
Fronius Smart Meter 1 phase
Fronius Smart Meter 3 phase
Catch Power Relay
Tesla Powerwall 2 powerwall2 Backup Gateway 2

Important

Site meters installed in the consumption path with batteries are not supported due to the inability to measure battery power. The site meter must be installed in the feedin path.

Configuration

The server uses a configuration JSON to configure how it works.

Inverters and meters

To configure the inverter and meter connections, add the following property to config.json

{
    "inverters": [ // (array) required: list of inverters
        {
            "type": "sunspec", // (string) required: the type of inverter
            "ip": "192.168.1.6", // (string) required: the IP address of the inverter
            "port": 502, // (number) required: the Modbus TCP port of the inverter
            "unitId": 1 // (number) required: the Modbus unit ID of the inverter
        }
    ],
    "inverterControl": true, // (true/false) optional: whether the inverters should be controlled based on limits, turn off to simulate
    "meter": {
        "type": "sunspec", // (string) required: the type of meter
        "ip": "192.168.1.6", // (string) required: the IP address of the meter
        "port": 502, // (number) required: the SunSpec Modbus TCP port of the meter
        "unitId": 240 // (number) required: the SunSpec unit ID of the meter
        "location": "feedin" // (string) optional: the location of the meter (feedin or consumption)
    }
    ...
}

Limiters

All limiters are restrictive, that is a combination of multiple limiters will evaluate all limiters and enforce the most prohibitive value of each control type at any one time.

Currently there are four control surfaces, mapped to the CSIP-AUS modes

Mode Description Overlap resolution Default value
opModConnect Connection to the grid Prioritize disconnect Connect
opModEnergize Generate or consume energy (in practice for most inverters this is the same as opModConnect) Prioritize de-energize Energized
opModExportLimW Maximum export limit (in watts) Lower limit Unlimited
opModGenLimW Maximum inverter generation limit (in watts) Lower limit Unlimited

Fixed limit

To set fixed limits (such as for fixed export limits), add the following property to config.json

{
    "limiters": {
        "fixed": {
            "connect": true, // (true/false) optional: whether the inverters should be connected to the grid
            "exportLimitWatts": 5000, // (number) optional: the maximum export limit in watts
            "generationLimitWatts": 10000 // (number) optional: the maximum generation limit in watts
        }
    }
    ...
}

Negative feed-in

To set a zero export limit based on negative feed-in, add the following property to config.json

For Amber Electric:

{
    "limiters": {
        "negativeFeedIn": {
            "type": "amber", // (string) required: the source of the negative feed-in data
            "apiKey": "asdf", // (string) required: the Amber API key
            "siteId": "12345" // (string) required: the Amber site ID
        }
    }
    ...
}

Two-way tariffs

To set a zero export limit based on two-way tariffs, add the following property to config.json

For Ausgrid EA029 tariff:

{
    "limiters": {
        "twoWayTariff": {
            "type": "ausgridEA029"
        }
    }
    ...
}

For SAPN RELE2W tariff:

{
    "limiters": {
        "twoWayTariff": {
            "type": "sapnRELE2W"
        }
    }
    ...
}

CSIP-AUS

Important

This CSIP-AUS client cannot run without device certificates (and manufacturer certificates issued by the utility server which must be manually registered) and is not provided in this repository. A future version of this application will support a self-service device registration process.

To use CSIP-AUS, add following property to config.json

{
    "limiters": {
        "sep2": {
            "host": "https://sep2-test.energyq.com.au", // (string) required: the SEP2 server host
            "dcapUri": "/api/v2/dcap" // (string) required: the device capability discovery URI
        }
    }
    ...
}

MQTT

To set limits based on a MQTT topic, add the following property to config.json

{
    "limiters": {
        "mqtt": {
            "host": "mqtt://192.168.1.123",
            "topic": "limits"
        }
    }
    ...
}

The MQTT topic must contain a JSON message that meets the following schema

z.object({
    opModConnect: z.boolean().optional(),
    opModEnergize: z.boolean().optional(),
    opModExpLimW: z.number().optional(),
    opModGenLimW: z.number().optional(),
});

Run server

Docker compose

  1. Clone repo

  2. Copy .env.example and rename it to .env and change the values to suit

  3. Create a /config folder and copy the config.example.json file from the repo and rename it to config.json. Set it with the required values.

  4. Run docker compose up -d (or run docker compose up -d --build)

CSIP-AUS client

The project implements a CSIP-AUS compatible client that interacts with the utility server (SEP2 server). The initial implementation focuses on the Energy Queensland requirements as outlined in the SEP2 Client Handbook published by Energy Queensland.

sequenceDiagram
    participant U as Utility<br>(CSIP-AUS server)
    participant SC as CSIP-AUS client
    participant C as Coordinator
    participant D as DER

    loop
    SC->>U: Discovery
    U->>SC: Devices, programs, DER controls
    SC->>U: Acknowledge DER controls
    end

    SC->>C: Control schedules<br> and limits

    loop
    D->>C: Metrics
    Note over C: Get current schedule<br>Calculate target power level<br>to meet limits
    C->>D: Inverter controls
       end

    loop
    SC->>U: Send DER status/capability/settings
    SC->>U: Send site and DER telemetry
    end

    box rgb(198,239,210) open-dynamic-export
    participant SC
    participant C
    end
Loading

The initial plan is to implement a direct gateway client that interacts directly with the utility server and the DER (solar inverters). The downside of a direct client approach is the registration process is manual and requires generating keys and certificates for each site/NMI. If the project is successful, a future version will allow self-service registration or a cloud-hosted aggregator proxy.

Features

  • Limits control
    • Fixed limits
    • Dynamic negative feed-in
      • Amber
      • Localvolts
    • Two-way tariffs
    • CSIP-AUS
  • Inverter integration
    • SunSpec Modbus TCP
  • Meter integration
    • SunSpec Modbus TCP
    • Tesla Powerwall
  • CSIP-AUS/SEP2/IEEE 2030.5 client
    • Discovery and scheduled entity polling
    • ConnectionPoint in-band registration
    • DER status/capability/settings reporting
    • DER control scheduling and default DER control fallback
    • Site/DER "mirror usage point" "mirror meter reading" reporting
    • Software-based limit ramping (setGradW or rampTms)
  • Metrics logging in InfluxDB

Future

  • CSIP-AUS self-service certificate generation
  • CSIP-AUS cloud aggregator proxy mode
  • Web UI with real-time metrics and historical metrics
  • Device package (plug and play solution)

CSIP-AUS Private key and CSR

CSIP-AUS uses PKI certificates to authorise and identify clients.

As a direct client, there needs to be two certificates, one for the "manufacturer" and one for the "device". The "manufacturer" certificate needs to be signed by the utility Smart Energy Root CA (SERCA). Then the "device" certificate is signed with the "manufacturer" certificate & key.

To generate a device certificate key and certificate signing request.

npm run cert:device-request

For local testing, generate a valid self signed certificate using

openssl req -x509 -new -key key.pem -out cert.pem -sha256 -days 3650 -nodes -subj "/"

For live testing, generate a valid device certificate by signing it with the manufacturer certificate.

npm run cert:device-generate

To view the device certificate LFDI

http://localhost:3000/csipAus/id

The manufacturer certificate is signed manually by the utility. The certificate key and certificate signing request can be generated with

openssl ecparam -name secp256r1 -genkey -noout -out mica_key.pem
openssl req -new -key mica_key.pem -out mica_cert_req.csr -sha256 -subj "/"

Motivation

My parents living in Queensland have a solar PV system and was required to move to Energex's dynamic connection to install an Tesla Powerwal battery because the total inverter capacity was >10kVA. A requirement of the dynamic connection is the use of a "complaint provider" (SEP2 client/device) to manage the solar inverters to meet dynamic export rules.

I opted for the CATCH Power Solar Relay solution since it was already installed at the site (for hot water control) and I wanted to support an Australian company. Unfortunately my experience with their product was subpar due to confusing UIs and a buggy implementation of SunSpec which does not support daisy chained Fronius inverters. I spent considerable time debugging their Modbus implementation and I tried to contact them to help improve their product but they were quite arrogant and not interested in my feedback.

So I thought I should put my efforts on making a better product that is open source since I have an interest in energy markets and was curious about the SEP2/CSIP-AUS standards.

I got in touch with Energy Queensland who was surprisingly helpful (for a government agency) and was open to the idea of an open-source client.

Resources

About

Dynamic export control/solar curtailment of inverters for dynamic connections (CSIP-AUS/SEP2/IEEE 2030.5), fixed/zero export or negative feed-in

Topics

Resources

License

Stars

Watchers

Forks

Languages