Reafctor app

This commit is contained in:
Oliver Traber 2024-04-13 22:43:03 +02:00
parent b8216f6ade
commit 7740be8bb5
Signed by: Bluemedia
GPG key ID: C0674B105057136C
36 changed files with 389 additions and 208 deletions

1
.gitignore vendored
View file

@ -1,3 +1,2 @@
**/__init__.py
**/__pycache__ **/__pycache__
simple-ocpp-cs.db simple-ocpp-cs.db

0
app/__init__.py Normal file
View file

View file

@ -15,3 +15,10 @@ else:
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base() Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

View file

@ -2,9 +2,11 @@ from fastapi import FastAPI
from starlette.middleware.authentication import AuthenticationMiddleware from starlette.middleware.authentication import AuthenticationMiddleware
import uvicorn import uvicorn
from routers import chargepoint_v1, ocpp_v1 from app.database import engine, Base
from database import engine, Base from app.models import *
from util.websocket_auth_backend import BasicAuthBackend
from app.routers import chargepoint_v1, ocpp_v1
from app.util.websocket_auth_backend import BasicAuthBackend
Base.metadata.create_all(bind=engine) Base.metadata.create_all(bind=engine)

8
app/models/__init__.py Normal file
View file

@ -0,0 +1,8 @@
__all__ = [
"chargepoint",
"connector",
"id_token",
"meter_value",
"transaction",
"user"
]

View file

@ -1,15 +1,16 @@
from sqlalchemy import Boolean, Column, DateTime, String import uuid
from sqlalchemy import Uuid, Boolean, Column, DateTime, String
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from database import Base from app.database import Base
class ChargePoint(Base): class ChargePoint(Base):
__tablename__ = "chargepoints" __tablename__ = "chargepoints"
id = Column(String, primary_key=True) id = Column(Uuid, primary_key=True, default=uuid.uuid4)
friendly_name = Column(String, unique=True, index=True) friendly_name = Column(String, unique=True, index=True)
is_active = Column(Boolean, default=True) is_active = Column(Boolean, default=True)
password = Column(String) password = Column(String)
last_seen = Column(DateTime, nullable=True) last_seen = Column(DateTime, nullable=True)
connectors = relationship("Connector") connectors = relationship("Connector", cascade="delete, delete-orphan")

15
app/models/connector.py Normal file
View file

@ -0,0 +1,15 @@
import uuid
from sqlalchemy import Uuid, Column, Enum, ForeignKey, Integer
from app.schemas.connector import ConnectorStatus
from app.database import Base
class Connector(Base):
__tablename__ = "connectors"
id = Column(Uuid, primary_key=True, default=uuid.uuid4)
evse = Column(Integer)
index = Column(Integer)
status = Column(Enum(ConnectorStatus))
chargepoint_id = Column(Uuid, ForeignKey("chargepoints.id"))

16
app/models/id_token.py Normal file
View file

@ -0,0 +1,16 @@
import uuid
from sqlalchemy import Uuid, Boolean, Column, ForeignKey, String
from sqlalchemy.orm import relationship
from app.database import Base
class IdToken(Base):
__tablename__ = "id_tokens"
id = Column(Uuid, primary_key=True, default=uuid.uuid4)
title = Column(String)
is_active = Column(Boolean, default=True)
token = Column(String, index=True)
owner_id = Column(Uuid, ForeignKey("users.id"))
owner = relationship("User", back_populates="id_tokens")

17
app/models/meter_value.py Normal file
View file

@ -0,0 +1,17 @@
import uuid
from sqlalchemy import Uuid, Column, DateTime, Enum, Float, ForeignKey, String
from app.database import Base
from app.schemas.meter_value import Measurand, PhaseType
class Transaction(Base):
__tablename__ = "meter_values"
id = Column(Uuid, primary_key=True, default=uuid.uuid4)
timestamp = Column(DateTime, index=True)
measurand = Column(Enum(Measurand))
phase_type = Column(Enum(PhaseType))
unit = Column(String)
value = Column(Float)
transaction_id = Column(Uuid, ForeignKey("transactions.id"), index=True)

20
app/models/transaction.py Normal file
View file

@ -0,0 +1,20 @@
import uuid
from sqlalchemy import Uuid, Column, DateTime, Enum, Float, ForeignKey
from sqlalchemy.orm import relationship, backref
from app.schemas.transaction import TransactionEventTriggerReason, TransactionStatus
from app.database import Base
class Transaction(Base):
__tablename__ = "transactions"
id = Column(Uuid, primary_key=True, default=uuid.uuid4)
status = Column(Enum(TransactionStatus), index=True)
started_at = Column(DateTime, index=True)
ended_at = Column(DateTime, nullable=True, index=True)
meter_start = Column(Float)
meter_end = Column(Float, nullable=True)
end_reason = Column(Enum(TransactionEventTriggerReason))
connector_id = Column(Uuid, ForeignKey("connectors.id"), index=True)
id_token_id = Column(Uuid, ForeignKey("id_tokens.id"), index= True)

14
app/models/user.py Normal file
View file

@ -0,0 +1,14 @@
import uuid
from sqlalchemy import Uuid, Boolean, Column, String
from sqlalchemy.orm import relationship
from app.database import Base
class User(Base):
__tablename__ = "users"
id = Column(Uuid, primary_key=True, default=uuid.uuid4)
friendly_name = Column(String, unique=True, index=True)
is_active = Column(Boolean, default=True)
id_tokens = relationship("IdToken", back_populates="owner", cascade="delete, delete-orphan")

View file

View file

@ -0,0 +1,88 @@
from datetime import datetime, UTC
import os
from ocpp.routing import on
from ocpp.v201 import ChargePoint as cp
from ocpp.v201 import call_result
from ocpp.v201.enums import Action, RegistrationStatusType, AuthorizationStatusType
from app.database import SessionLocal
from app.models.chargepoint import ChargePoint
from app.models.connector import Connector
from app.models.id_token import IdToken
from app.schemas.connector import ConnectorStatus
class ChargePoint(cp):
@on(Action.BootNotification)
async def on_boot_notification(self, charging_station, reason, **kwargs):
with SessionLocal() as db:
db_chargepoint = db.query(ChargePoint).filter(ChargePoint.friendly_name == self.id).first()
db_chargepoint.last_seen = datetime.now(UTC)
db.commit()
return call_result.BootNotificationPayload(
current_time=datetime.now(UTC).isoformat(),
interval=int(os.getenv("CS_HEARTBEAT_INTERVAL", "1800")),
status=RegistrationStatusType.accepted
)
@on(Action.Heartbeat)
async def on_heartbeat_request(self):
with SessionLocal() as db:
db_chargepoint = db.query(ChargePoint).filter(ChargePoint.friendly_name == self.id).first()
db_chargepoint.last_seen = datetime.now(UTC)
db.commit()
return call_result.HeartbeatPayload(
current_time=datetime.now(UTC).isoformat()
)
@on(Action.StatusNotification)
async def on_status_notification(self, evse_id: int, connector_id: int, connector_status: str, **kwargs):
with SessionLocal() as db:
db_chargepoint = db.query(ChargePoint).filter(ChargePoint.friendly_name == self.id).first()
db_chargepoint.last_seen = datetime.now(UTC)
db_connector = db.query(Connector).filter(
Connector.chargepoint_id == db_chargepoint.id,
Connector.evse == evse_id,
Connector.index == connector_id
).first()
if db_connector == None:
db_connector = Connector(
chargepoint_id = db_chargepoint.id,
evse = evse_id,
index = connector_id,
status = ConnectorStatus(connector_status)
)
db.add(db_connector)
else:
db_connector.status = ConnectorStatus(connector_status)
db.commit()
return call_result.StatusNotificationPayload()
@on(Action.Authorize)
async def on_authorize(self, id_token, **kwargs):
if id_token == None:
return call_result.AuthorizePayload(id_token_info={'status': AuthorizationStatusType.invalid})
if id_token.type != "ISO14443" | "ISO15693":
return call_result.AuthorizePayload(id_token_info={'status': AuthorizationStatusType.invalid})
with SessionLocal() as db:
db_chargepoint = db.query(ChargePoint).filter(ChargePoint.friendly_name == self.id).first()
db_chargepoint.last_seen = datetime.now(UTC)
db_id_token = db.query(IdToken).filter(IdToken.token == id_token.id).first()
db.commit()
if db_id_token == None:
return call_result.AuthorizePayload(id_token_info={'status': AuthorizationStatusType.unknown})
if db_id_token.is_active == False:
return call_result.AuthorizePayload(id_token_info={'status': AuthorizationStatusType.blocked})
return call_result.AuthorizePayload(id_token_info={'status': AuthorizationStatusType.accepted, 'groupIdToken': str(db_id_token.owner_id)})
@on(Action.TransactionEvent)
async def on_transaction_event(self):
return call_result.TransactionEventPayload()

View file

@ -1,22 +1,23 @@
import logging import logging
from typing import Any, Coroutine, Dict from typing import Any, Coroutine, Dict
from uuid import UUID
from websockets import ConnectionClosed from websockets import ConnectionClosed
from ocpp_proto.chargepoint import ChargePoint from app.ocpp_proto.chargepoint import ChargePoint
__active_connections: Dict[str, ChargePoint] = {} __active_connections: Dict[UUID, ChargePoint] = {}
async def start(cp: ChargePoint): async def start(id: UUID, cp: ChargePoint):
try: try:
__active_connections[cp.id] = cp __active_connections[id] = cp
await cp.start() await cp.start()
except ConnectionClosed: except ConnectionClosed:
logging.info("Charging station '%s' disconnected", cp.id) logging.info("Charging station '%s' (%s) disconnected", cp.id, id)
__active_connections.pop(cp.id, None) __active_connections.pop(id, None)
async def call( async def call(
chargepoint_id: str, chargepoint_id: UUID,
payload: Any, payload: Any,
suppress: bool = True, suppress: bool = True,
unique_id: Any | None = None unique_id: Any | None = None
@ -27,5 +28,5 @@ async def call(
except KeyError as e: except KeyError as e:
raise e raise e
def is_connected(chargepoint_id: str): def is_connected(chargepoint_id: UUID):
return chargepoint_id in __active_connections.keys() return chargepoint_id in __active_connections.keys()

0
app/routers/__init__.py Normal file
View file

View file

@ -0,0 +1,114 @@
import random
import string
from uuid import UUID
from fastapi import APIRouter, HTTPException, Security
from fastapi.params import Depends
from sqlalchemy.orm import Session
from app.database import get_db
from app.ocpp_proto import chargepoint_manager
from app.schemas.chargepoint import ChargePoint, ChargePointCreate, ChargePointUpdate, ChargePointPassword, ChargePointConnectionInfo
from app.models.chargepoint import ChargePoint as DBChargePoint
from app.security import get_api_key
router = APIRouter(
prefix="/chargepoint",
tags=["chargepoint"],
)
@router.get(path="/", response_model=list[ChargePoint])
async def get_chargepoints(
skip: int = 0,
limit: int = 20,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
):
return db.query(DBChargePoint).offset(skip).limit(limit).all()
@router.get(path="/{chargepoint_id}", response_model=ChargePoint)
async def get_chargepoint(
chargepoint_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
):
chargepoint = db.query(DBChargePoint).filter(DBChargePoint.id == chargepoint_id).first()
if chargepoint is None:
raise HTTPException(status_code=404, detail="Chargepoint not found")
return chargepoint
@router.get(path="/{chargepoint_id}/password", response_model=ChargePointPassword)
async def get_chargepoint_password(
chargepoint_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
):
chargepoint = db.query(DBChargePoint).filter(DBChargePoint.id == chargepoint_id).first()
if chargepoint is None:
raise HTTPException(status_code=404, detail="Chargepoint not found")
return ChargePointPassword(password=chargepoint.password)
@router.delete(path="/{chargepoint_id}/password", response_model=ChargePointPassword)
async def reset_chargepoint_password(
chargepoint_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
):
chargepoint = db.query(DBChargePoint).filter(DBChargePoint.id == chargepoint_id).first()
if chargepoint is None:
raise HTTPException(status_code=404, detail="Chargepoint not found")
chargepoint.password = ''.join(random.choice(string.ascii_letters + string.digits) for i in range(24))
db.commit()
return ChargePointPassword(password=chargepoint.password)
@router.post(path="/", response_model=ChargePoint)
async def create_chargepoint(
chargepoint: ChargePointCreate,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
):
chargepoint_db = DBChargePoint(
friendly_name=chargepoint.friendly_name,
is_active=chargepoint.is_active,
password=''.join(random.choice(string.ascii_letters + string.digits) for i in range(24))
)
db.add(chargepoint_db)
db.commit()
db.refresh(chargepoint_db)
return chargepoint_db
@router.patch(path="/{chargepoint_id}", response_model=ChargePoint)
async def update_chargepoint(
chargepoint_id: UUID,
chargepoint_update: ChargePointUpdate,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
):
chargepoint = db.query(DBChargePoint).filter(DBChargePoint.id == chargepoint_id).first()
if chargepoint is None:
raise HTTPException(status_code=404, detail="Chargepoint not found")
for key, value in chargepoint_update.model_dump(exclude_unset=True).items():
setattr(chargepoint, key, value)
db.commit()
return chargepoint
@router.delete(path="/{chargepoint_id}", response_model=[])
async def delete_chargepoint(
chargepoint_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
):
chargepoint = db.query(DBChargePoint).filter(DBChargePoint.id == chargepoint_id).first()
if chargepoint is None:
raise HTTPException(status_code=404, detail="Chargepoint not found")
db.delete(chargepoint)
db.commit()
return []
@router.get(path="/{chargepoint_id}/status", response_model=ChargePointConnectionInfo)
async def get_chargepoint_status(
chargepoint_id: UUID,
api_key: str = Security(get_api_key)
):
return ChargePointConnectionInfo(
connected=chargepoint_manager.is_connected(chargepoint_id)
)

View file

@ -1,24 +1,25 @@
import logging import logging
from fastapi import APIRouter, WebSocket, WebSocketException from fastapi import APIRouter, WebSocket, WebSocketException
from ocpp_proto import chargepoint_manager
from ocpp_proto.chargepoint import ChargePoint from app.ocpp_proto import chargepoint_manager
from util.websocket_wrapper import WebSocketWrapper from app.ocpp_proto.chargepoint import ChargePoint
from app.util.websocket_wrapper import WebSocketWrapper
router = APIRouter() router = APIRouter()
@router.websocket("/{charging_station_id}") @router.websocket("/{charging_station_friendly_name}")
async def websocket_endpoint( async def websocket_endpoint(
*, *,
websocket: WebSocket, websocket: WebSocket,
charging_station_id: str, charging_station_friendly_name: str,
): ):
""" For every new charging station that connects, create a ChargePoint """ For every new charging station that connects, create a ChargePoint
instance and start listening for messages. instance and start listening for messages.
""" """
if (websocket.user.username != charging_station_id): if (websocket.user.friendly_name != charging_station_friendly_name):
raise WebSocketException(code=1008, reason="Username doesn't match chargepoint identifier") raise WebSocketException(code=1008, reason="Username doesn't match chargepoint identifier")
logging.info("Charging station '%s' connected", charging_station_id) logging.info("Charging station '%s' (%s) connected", charging_station_friendly_name, websocket.user.id)
# Check protocols # Check protocols
try: try:
@ -42,5 +43,5 @@ async def websocket_endpoint(
# Accept connection and begin communication # Accept connection and begin communication
await websocket.accept(subprotocol="ocpp2.0.1") await websocket.accept(subprotocol="ocpp2.0.1")
cp = ChargePoint(charging_station_id, WebSocketWrapper(websocket)) cp = ChargePoint(charging_station_friendly_name, WebSocketWrapper(websocket))
await chargepoint_manager.start(cp) await chargepoint_manager.start(websocket.user.id, cp)

0
app/schemas/__init__.py Normal file
View file

View file

@ -0,0 +1,31 @@
from datetime import datetime
from typing import Optional
from uuid import UUID
from pydantic import BaseModel
from app.schemas.connector import Connector
class ChargePointBase(BaseModel):
friendly_name: str
is_active: bool
class ChargePointUpdate(BaseModel):
friendly_name: Optional[str] = None
is_active: Optional[bool] = None
class ChargePointCreate(ChargePointBase):
pass
class ChargePoint(ChargePointBase):
id: UUID
last_seen: datetime | None
connectors: list[Connector] = []
class Config:
from_attributes = True
class ChargePointPassword(BaseModel):
password: str
class ChargePointConnectionInfo(BaseModel):
connected: bool

View file

@ -1,4 +1,5 @@
import enum import enum
from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel
class ConnectorStatus(enum.Enum): class ConnectorStatus(enum.Enum):
@ -9,7 +10,7 @@ class ConnectorStatus(enum.Enum):
FAULTED = "Faulted" FAULTED = "Faulted"
class Connector(BaseModel): class Connector(BaseModel):
id: str id: UUID
evse: int evse: int
index: int index: int
status: ConnectorStatus status: ConnectorStatus

View file

@ -1,6 +1,7 @@
from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel
from schemas.user import User from app.schemas.user import User
class IdTokenBase(BaseModel): class IdTokenBase(BaseModel):
title: str title: str
@ -10,7 +11,7 @@ class IdTokenCreate(IdTokenBase):
pass pass
class IdToken(IdTokenBase): class IdToken(IdTokenBase):
id: str id: UUID
owner: User owner: User
class Config: class Config:

View file

@ -1,5 +1,6 @@
from datetime import datetime from datetime import datetime
import enum import enum
from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel
class PhaseType(enum.Enum): class PhaseType(enum.Enum):
@ -42,7 +43,7 @@ class Measurand(enum.Enum):
VOLTAGE = "Voltage" VOLTAGE = "Voltage"
class MeterValue(BaseModel): class MeterValue(BaseModel):
id: str id: UUID
timestamp: datetime timestamp: datetime
measurand: Measurand measurand: Measurand
phase_type: PhaseType phase_type: PhaseType

View file

@ -1,5 +1,6 @@
from datetime import datetime from datetime import datetime
import enum import enum
from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel
class TransactionStatus(enum.Enum): class TransactionStatus(enum.Enum):
@ -30,7 +31,7 @@ class TransactionEventTriggerReason(enum.Enum):
RESET_COMMAND = "ResetCommand" RESET_COMMAND = "ResetCommand"
class Transaction(BaseModel): class Transaction(BaseModel):
id: str id: UUID
status: TransactionStatus status: TransactionStatus
started_at: datetime started_at: datetime
ended_at: datetime ended_at: datetime

View file

@ -1,6 +1,7 @@
from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel
from schemas.id_token import IdToken from app.schemas.id_token import IdToken
class UserBase(BaseModel): class UserBase(BaseModel):
friendly_name: str friendly_name: str
@ -10,7 +11,7 @@ class UserCreate(UserBase):
pass pass
class User(UserBase): class User(UserBase):
id: str id: UUID
id_tokens: list[IdToken] = [] id_tokens: list[IdToken] = []
class Config: class Config:

0
app/util/__init__.py Normal file
View file

View file

@ -1,9 +1,13 @@
import base64 import base64
import binascii import binascii
from uuid import UUID
from starlette.authentication import ( from starlette.authentication import (
AuthCredentials, AuthenticationBackend, AuthenticationError, SimpleUser AuthCredentials, AuthenticationBackend, AuthenticationError, SimpleUser
) )
from app.database import SessionLocal
from app.models.chargepoint import ChargePoint
class BasicAuthBackend(AuthenticationBackend): class BasicAuthBackend(AuthenticationBackend):
async def authenticate(self, conn): async def authenticate(self, conn):
if "Authorization" not in conn.headers: if "Authorization" not in conn.headers:
@ -19,5 +23,16 @@ class BasicAuthBackend(AuthenticationBackend):
raise AuthenticationError('Invalid basic auth credentials') raise AuthenticationError('Invalid basic auth credentials')
username, _, password = decoded.partition(":") username, _, password = decoded.partition(":")
# TODO: You'd want to verify the username and password here. try:
return AuthCredentials(["authenticated"]), SimpleUser(username) id = UUID(username)
except (ValueError) as exc:
raise AuthenticationError('Invalid basic auth credentials')
with SessionLocal() as db:
chargepoint = db.query(ChargePoint).filter(ChargePoint.id == id).first()
if chargepoint is None:
raise AuthenticationError('Invalid basic auth credentials')
if chargepoint.password != password:
raise AuthenticationError('Invalid basic auth credentials')
return AuthCredentials(["authenticated"]), chargepoint

View file

@ -1,14 +0,0 @@
from sqlalchemy import Column, Enum, ForeignKey, Integer, String
from schemas.connector import ConnectorStatus
from database import Base
class Connector(Base):
__tablename__ = "connectors"
id = Column(String, primary_key=True)
evse = Column(Integer)
index = Column(Integer)
status = Column(Enum(ConnectorStatus))
chargepoint_id = Column(String, ForeignKey("chargepoints.id"))

View file

@ -1,14 +0,0 @@
from sqlalchemy import Boolean, Column, ForeignKey, String
from sqlalchemy.orm import relationship
from database import Base
class IdToken(Base):
__tablename__ = "id_tokens"
id = Column(String, primary_key=True)
title = Column(String)
is_active = Column(Boolean, default=True)
owner_id = Column(String, ForeignKey("users.id"))
owner = relationship("User", back_populates="id_tokens")

View file

@ -1,16 +0,0 @@
from sqlalchemy import Column, DateTime, Enum, Float, ForeignKey, String
from database import Base
from schemas.meter_value import Measurand, PhaseType
class Transaction(Base):
__tablename__ = "meter_values"
id = Column(String, primary_key=True)
timestamp = Column(DateTime, index=True)
measurand = Column(Enum(Measurand))
phase_type = Column(Enum(PhaseType))
unit = Column(String)
value = Column(Float)
transaction_id = Column(String, ForeignKey("transactions.id", index=True))

View file

@ -1,19 +0,0 @@
import enum
from sqlalchemy import Column, DateTime, Enum, Float, ForeignKey, String
from schemas.transaction import TransactionEventTriggerReason, TransactionStatus
from database import Base
class Transaction(Base):
__tablename__ = "transactions"
id = Column(String, primary_key=True)
status = Column(Enum(TransactionStatus), index=True)
started_at = Column(DateTime, index=True)
ended_at = Column(DateTime, nullable=True, index=True)
meter_start = Column(Float)
meter_end = Column(Float, nullable=True)
end_reason = Column(Enum(TransactionEventTriggerReason))
connector_id = Column(String, ForeignKey("connectors.id", index=True))
id_token_id = Column(String, ForeignKey("id_tokens.id", index= True))

View file

@ -1,12 +0,0 @@
from sqlalchemy import Boolean, Column, String
from sqlalchemy.orm import relationship
from database import Base
class User(Base):
__tablename__ = "users"
id = Column(String, primary_key=True)
friendly_name = Column(String, unique=True, index=True)
is_active = Column(Boolean, default=True)
id_tokens = relationship("IdToken", back_populates="owner")

View file

@ -1,33 +0,0 @@
from datetime import datetime, UTC
import os
from ocpp.routing import on
from ocpp.v201 import ChargePoint as cp
from ocpp.v201 import call_result
from ocpp.v201.enums import Action, RegistrationStatusType, AuthorizationStatusType
class ChargePoint(cp):
@on(Action.BootNotification)
async def on_boot_notification(self, charging_station, reason, **kwargs):
return call_result.BootNotificationPayload(
current_time=datetime.now(UTC).isoformat(),
interval=int(os.getenv("CS_HEARTBEAT_INTERVAL", "1800")),
status=RegistrationStatusType.accepted
)
@on(Action.Heartbeat)
async def on_heartbeat_request(self):
return call_result.HeartbeatPayload(
current_time=datetime.now(UTC).isoformat()
)
@on(Action.StatusNotification)
async def on_status_notification(self, evse_id, connector_id, connector_status, **kwargs):
return call_result.StatusNotificationPayload()
@on(Action.Authorize)
async def on_authorize(self):
return call_result.AuthorizePayload(id_token_info={'status': AuthorizationStatusType.accepted, 'groupIdToken': ""})
@on(Action.TransactionEvent)
async def on_transaction_event(self):
return call_result.TransactionEventPayload()

View file

@ -1,45 +0,0 @@
from fastapi import APIRouter, Security
from ocpp_proto import chargepoint_manager
from schemas.chargepoint import ChargePoint
from security import get_api_key
router = APIRouter(
prefix="/chargepoint",
tags=["chargepoint"],
)
@router.get(path="/", response_model=list[ChargePoint])
async def get_chargepoints(api_key: str = Security(get_api_key)):
return
@router.post(path="/", response_model=ChargePoint)
async def create_chargepoint(api_key: str = Security(get_api_key)):
return
@router.get(path="/{chargepoint_id}", response_model=ChargePoint)
async def get_chargepoint(
chargepoint_id: str,
api_key: str = Security(get_api_key),
):
return
@router.patch(path="/{chargepoint_id}", response_model=ChargePoint)
async def update_chargepoint(
chargepoint_id: str,
api_key: str = Security(get_api_key),
):
return
@router.delete(path="/{chargepoint_id}", response_model=[])
async def delete_chargepoint(
chargepoint_id: str,
api_key: str = Security(get_api_key),
):
return
@router.get(path="/{chargepoint_id}/status", response_model=bool)
async def get_chargepoint(
chargepoint_id: str,
api_key: str = Security(get_api_key),
):
return chargepoint_manager.is_connected(chargepoint_id)

View file

@ -1,20 +0,0 @@
from datetime import datetime
from pydantic import BaseModel
from schemas.connector import Connector
class ChargePointBase(BaseModel):
friendly_name: str
is_active: bool
class ChargePointCreate(ChargePointBase):
pass
class ChargePoint(ChargePointBase):
id: str
password: str
last_seen: datetime
connectors: list[Connector] = []
class Config:
from_attributes = True