Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ Typically settings are set by setting an environment variable with the same name
| **Setting** | **Type** | **Purpose** |
| ----------- | -------- | ----------- |
| `cert_header` | `string` | The name of the HTTP header that API endpoints will look for to validate a client. This should be set by the TLS termination point and can contain either a full client certificate in PEM format or the sha256 fingerprint of that certificate. defaults to "x-forwarded-client-cert" |
| `default_doe_import_active_watts` | `float` | If set - the DefaultDERControl endpoint will be activated with the DOE extensions for import being set to this value (requires `default_doe_export_active_watts`)|
| `default_doe_export_active_watts` | `float` | If set - the DefaultDERControl endpoint will be activated with the DOE extensions for export being set to this value (requires `default_doe_import_active_watts`)|
| `allow_device_registration` | `bool` | If True - the registration workflows that enable unrecognised certs to generate/manage a single EndDevice (tied to that cert) will be enabled. Otherwise any cert will need to be registered out of band and assigned to an aggregator before connections can be made. Defaults to False|
| `static_registration_pin` | `int` | If set - all new EndDevice registrations will have their Registration PIN set to this value (use 5 digit form). Uses a random number generator otherwise. |
| `nmi_validation_enabled` | `bool` | If `true` - all updates of `ConnectionPoint` resource will trigger validation on `ConnectionPoint.id` against on AEMO's NMI Allocation List (Version 13 – November 2022). Defaults to `false`. |
Expand Down
118 changes: 59 additions & 59 deletions postman/envoy-admin-server.postman_collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -407,65 +407,6 @@
},
"response": []
},
{
"name": "POST Update site control default",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"import_limit_watts\": {\"value\": 500},\n \"export_limit_watts\": {\"value\": 500},\n \"generation_limit_watts\": {\"value\": 500},\n \"load_limit_watts\": {\"value\": 500},\n \"ramp_rate_percent_per_second\": {\"value\": 500}\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{HOST}}/site/{{SITE_ID}}/control_default",
"host": [
"{{HOST}}"
],
"path": [
"site",
"{{SITE_ID}}",
"control_default"
]
}
},
"response": []
},
{
"name": "GET site control default",
"protocolProfileBehavior": {
"disableBodyPruning": true
},
"request": {
"method": "GET",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"import_limit_watts\" 500,\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{HOST}}/site/{{SITE_ID}}/control_default",
"host": [
"{{HOST}}"
],
"path": [
"site",
"{{SITE_ID}}",
"control_default"
]
}
},
"response": []
},
{
"name": "POST Update server runtime config",
"request": {
Expand Down Expand Up @@ -791,6 +732,65 @@
},
"response": []
},
{
"name": "POST Update site control group default",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"import_limit_watts\": {\"value\": 500},\n \"export_limit_watts\": {\"value\": 500},\n \"generation_limit_watts\": {\"value\": 500},\n \"load_limit_watts\": {\"value\": 500},\n \"ramp_rate_percent_per_second\": {\"value\": 500}\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{HOST}}/site_control_group/{{GROUP_ID}}/default",
"host": [
"{{HOST}}"
],
"path": [
"site_control_group",
"{{GROUP_ID}}",
"default"
]
}
},
"response": []
},
{
"name": "GET site control group default",
"protocolProfileBehavior": {
"disableBodyPruning": true
},
"request": {
"method": "GET",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"import_limit_watts\" 500,\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{HOST}}/site_control_group/{{GROUP_ID}}/default",
"host": [
"{{HOST}}"
],
"path": [
"site_control_group",
"{{GROUP_ID}}",
"default"
]
}
},
"response": []
},
{
"name": "POST Site controls",
"request": {
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ addopts = "--strict-markers"
markers = [
"cert_header: marks tests to use a custom value for cert_header instead of the default",
"href_prefix: marks tests to use a custom value for href_prefix instead of the default of None",
"no_default_doe: marks tests to disable the default DOE config values (disables default DERControl endpoint)",
"azure_ad_auth: marks tests to enable the azure active directory auth dependency",
"azure_ad_db: marks tests to enable the azure active directory dynamic db creds dependency (requires azure_ad_auth)",
"azure_ad_db_refresh_secs: marks tests to set the config value azure_ad_db_refresh_secs (requires azure_ad_db)",
Expand Down Expand Up @@ -50,7 +49,7 @@ name = "envoy"
dynamic = ["version", "readme"]
requires-python = ">=3.9,<4.0"
dependencies = [
"envoy_schema==0.30.0",
"envoy_schema==0.31.0",
"fastapi>=0.94.1",
"sqlalchemy>=2.0.0",
"alembic",
Expand Down
38 changes: 2 additions & 36 deletions src/envoy/admin/api/config.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import logging
from http import HTTPStatus

from envoy_schema.admin.schema.config import (
ControlDefaultRequest,
ControlDefaultResponse,
RuntimeServerConfigRequest,
RuntimeServerConfigResponse,
)
from envoy_schema.admin.schema.uri import ServerConfigRuntimeUri, SiteControlDefaultConfigUri
from envoy_schema.admin.schema.config import RuntimeServerConfigRequest, RuntimeServerConfigResponse
from envoy_schema.admin.schema.uri import ServerConfigRuntimeUri
from fastapi import APIRouter
from fastapi_async_sqlalchemy import db

from envoy.admin.manager.config import ConfigManager
from envoy.server.api.error_handler import LoggedHttpException

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -40,31 +34,3 @@ async def get_runtime_config() -> RuntimeServerConfigResponse:
RuntimeServerConfigResponse
"""
return await ConfigManager.fetch_config_response(db.session)


@router.post(SiteControlDefaultConfigUri, status_code=HTTPStatus.NO_CONTENT, response_model=None)
async def update_site_control_default(site_id: int, body: ControlDefaultRequest) -> None:
"""Updates the control default config for the specified site. Any missing values will NOT be updated.

Body:
single ControlDefaultRequest object.

Returns:
None
"""
result = await ConfigManager.update_site_control_default(db.session, site_id, body)
if not result:
raise LoggedHttpException(logger, None, HTTPStatus.NOT_FOUND, f"site_id {site_id} not found")


@router.get(SiteControlDefaultConfigUri, status_code=HTTPStatus.OK, response_model=ControlDefaultResponse)
async def get_site_control_default(site_id: int) -> ControlDefaultResponse:
"""Gets the control default config for the specified site.

Returns:
ControlDefaultResponse or 404
"""
result = await ConfigManager.fetch_site_control_default_response(db.session, site_id)
if not result:
raise LoggedHttpException(logger, None, HTTPStatus.NOT_FOUND, f"site_id {site_id} not found")
return result
31 changes: 31 additions & 0 deletions src/envoy/admin/api/site_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@

from asyncpg.exceptions import CardinalityViolationError # type: ignore
from envoy_schema.admin.schema.site_control import (
SiteControlGroupDefaultRequest,
SiteControlGroupDefaultResponse,
SiteControlGroupPageResponse,
SiteControlGroupRequest,
SiteControlGroupResponse,
SiteControlPageResponse,
SiteControlRequest,
)
from envoy_schema.admin.schema.uri import (
SiteControlGroupDefaultUri,
SiteControlGroupListUri,
SiteControlGroupUri,
SiteControlRangeUri,
Expand Down Expand Up @@ -145,3 +148,31 @@ async def delete_site_controls_in_range(group_id: int, period_start: datetime, p
await SiteControlListManager.delete_site_controls_in_range(
db.session, site_control_group_id=group_id, site_id=None, period_start=period_start, period_end=period_end
)


@router.post(SiteControlGroupDefaultUri, status_code=HTTPStatus.NO_CONTENT, response_model=None)
async def update_site_control_default(group_id: int, body: SiteControlGroupDefaultRequest) -> None:
"""Updates the control default config for the specified SiteControlGroup. Any missing values will NOT be updated.

Body:
single SiteControlGroupDefaultRequest object.

Returns:
None
"""
result = await SiteControlGroupManager.update_site_control_default(db.session, group_id, body)
if not result:
raise LoggedHttpException(logger, None, HTTPStatus.NOT_FOUND, f"group_id {group_id} not found")


@router.get(SiteControlGroupDefaultUri, status_code=HTTPStatus.OK, response_model=SiteControlGroupDefaultResponse)
async def get_site_control_default(group_id: int) -> SiteControlGroupDefaultResponse:
"""Gets the control default config for the specified SiteControlGroup

Returns:
SiteControlGroupDefaultResponse or 404
"""
result = await SiteControlGroupManager.fetch_site_control_default_response(db.session, group_id)
if not result:
raise LoggedHttpException(logger, None, HTTPStatus.NOT_FOUND, f"group_id {group_id} not found")
return result
4 changes: 0 additions & 4 deletions src/envoy/admin/crud/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ async def select_single_site_no_scoping(
site_id: int,
include_groups: bool = False,
include_der: bool = False,
include_site_default: bool = False,
) -> Optional[Site]:
"""Admin selecting of a single site - no filtering on aggregator is made."""

Expand All @@ -136,8 +135,5 @@ async def select_single_site_no_scoping(
selectinload(Site.site_ders).selectinload(SiteDER.site_der_status),
)

if include_site_default:
stmt = stmt.options(selectinload(Site.default_site_control))

resp = await session.execute(stmt)
return resp.scalar_one_or_none()
82 changes: 2 additions & 80 deletions src/envoy/admin/manager/config.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
from datetime import datetime, timezone
from decimal import Decimal
from typing import Optional

from envoy_schema.admin.schema.config import (
ControlDefaultRequest,
ControlDefaultResponse,
RuntimeServerConfigRequest,
RuntimeServerConfigResponse,
)

from envoy_schema.admin.schema.config import RuntimeServerConfigRequest, RuntimeServerConfigResponse
from sqlalchemy.ext.asyncio import AsyncSession

from envoy.admin.crud.site import select_single_site_no_scoping
from envoy.notification.manager.notification import NotificationManager
from envoy.server.crud.server import select_server_config
from envoy.server.manager.server import _map_server_config
from envoy.server.manager.time import utc_now
from envoy.server.model.server import RuntimeServerConfig as ConfigEntity
from envoy.server.model.site import DefaultSiteControl
from envoy.server.model.subscription import SubscriptionResource


Expand Down Expand Up @@ -100,72 +91,3 @@ async def fetch_config_response(session: AsyncSession) -> RuntimeServerConfigRes
changed_time=changed_time,
created_time=created_time,
)

@staticmethod
async def update_site_control_default(session: AsyncSession, site_id: int, request: ControlDefaultRequest) -> bool:
now = utc_now()

site = await select_single_site_no_scoping(session, site_id, include_site_default=True)
if site is None:
return False

if site.default_site_control is None:
site.default_site_control = DefaultSiteControl(changed_time=now, site_id=site.site_id, version=0)
else:
site.default_site_control.changed_time = now

if request.import_limit_watts is not None:
site.default_site_control.import_limit_active_watts = request.import_limit_watts.value

if request.export_limit_watts is not None:
site.default_site_control.export_limit_active_watts = request.export_limit_watts.value

if request.generation_limit_watts is not None:
site.default_site_control.generation_limit_active_watts = request.generation_limit_watts.value

if request.load_limit_watts is not None:
site.default_site_control.load_limit_active_watts = request.load_limit_watts.value

if request.ramp_rate_percent_per_second is not None:
ramp_rate_value = (
int(request.ramp_rate_percent_per_second.value)
if request.ramp_rate_percent_per_second.value is not None
else None
)
site.default_site_control.ramp_rate_percent_per_second = ramp_rate_value

site.default_site_control.version = site.default_site_control.version + 1
await session.commit()

await NotificationManager.notify_changed_deleted_entities(SubscriptionResource.DEFAULT_SITE_CONTROL, now)

return True

@staticmethod
async def fetch_site_control_default_response(
session: AsyncSession, site_id: int
) -> Optional[ControlDefaultResponse]:
"""Fetches the current site control default values as a ControlDefaultResponse for external communication"""
site = await select_single_site_no_scoping(session, site_id, include_site_default=True)
if not site:
return None
if site.default_site_control:
default_config = site.default_site_control
else:
default_config = DefaultSiteControl(
changed_time=site.changed_time, created_time=site.created_time, site_id=site.site_id
)

return ControlDefaultResponse(
ramp_rate_percent_per_second=(
Decimal(default_config.ramp_rate_percent_per_second)
if default_config.ramp_rate_percent_per_second is not None
else None
),
server_default_import_limit_watts=default_config.import_limit_active_watts,
server_default_export_limit_watts=default_config.export_limit_active_watts,
server_default_generation_limit_watts=default_config.generation_limit_active_watts,
server_default_load_limit_watts=default_config.load_limit_active_watts,
changed_time=default_config.changed_time,
created_time=default_config.created_time,
)
Loading
Loading