Compare commits
8 commits
feature/us
...
main
Author | SHA1 | Date | |
---|---|---|---|
f0eff338ff | |||
b59aeeb5e5 | |||
486977f828 | |||
65183e26c0 | |||
46b5040099 | |||
e5c5e94989 | |||
44e6f85da1 | |||
50972c209e |
16 changed files with 310 additions and 12 deletions
|
@ -9,6 +9,7 @@ from typing import Sequence, Union
|
|||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
@ -31,16 +32,23 @@ def upgrade() -> None:
|
|||
)
|
||||
op.create_index(op.f('ix_sessions_refresh_token'), 'sessions', ['refresh_token'], unique=True)
|
||||
op.create_index(op.f('ix_sessions_user_id'), 'sessions', ['user_id'], unique=False)
|
||||
|
||||
op.add_column('users', sa.Column('email', sa.String(), nullable=True))
|
||||
op.add_column('users', sa.Column('password', sa.String(), nullable=True))
|
||||
op.add_column('users', sa.Column('role', sa.Enum('MEMBER', 'ADMINISTRATOR', name='role'), nullable=True))
|
||||
|
||||
role_enum = postgresql.ENUM('MEMBER', 'ADMINISTRATOR', name='role')
|
||||
role_enum.create(op.get_bind(), checkfirst=False)
|
||||
op.add_column('users', sa.Column('role', type_=role_enum, nullable=True))
|
||||
|
||||
op.execute('UPDATE users SET email = id || \'@example.com\'')
|
||||
op.execute('UPDATE users SET password = \'invalid\'')
|
||||
op.execute('UPDATE users SET role = \'MEMBER\'')
|
||||
|
||||
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||
batch_op.alter_column('email', nullable=False)
|
||||
batch_op.alter_column('password', nullable=False)
|
||||
batch_op.alter_column('role', nullable=False)
|
||||
|
||||
op.drop_index('ix_users_friendly_name', table_name='users')
|
||||
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
"""Add firmware_update table
|
||||
|
||||
Revision ID: 00edfb13e611
|
||||
Revises: c7f72154c90b
|
||||
Create Date: 2025-03-23 14:01:14.029527+00:00
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '00edfb13e611'
|
||||
down_revision: Union[str, None] = 'c7f72154c90b'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('firmware_updates',
|
||||
sa.Column('id', sa.Uuid(), nullable=False),
|
||||
sa.Column('request_id', sa.Integer(), nullable=True),
|
||||
sa.Column('status', sa.Enum('CREATED', 'SUBMITTED', 'DOWNLOADED', 'DOWNLOAD_FAILED', 'DOWNLOADING', 'DOWNLOAD_SCHEDULED', 'DOWNLOAD_PAUSED', 'IDLE', 'INSTALLATION_FAILED', 'INSTALLING', 'INSTALLED', 'INSTALL_REBOOTING', 'INSTALL_SCHEDULED', 'INSTALL_VERIFICATION_FAILED', 'INVALID_SIGNATURE', 'SIGNATURE_VERIFIED', name='firmwareupdatestatus'), nullable=True),
|
||||
sa.Column('retries', sa.Integer(), nullable=True),
|
||||
sa.Column('retry_interval', sa.Integer(), nullable=True),
|
||||
sa.Column('location', sa.String(), nullable=True),
|
||||
sa.Column('retrieve_date_time', sa.DateTime(), nullable=True),
|
||||
sa.Column('install_date_time', sa.DateTime(), nullable=True),
|
||||
sa.Column('chargepoint_id', sa.Uuid(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['chargepoint_id'], ['chargepoints.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_firmware_updates_chargepoint_id'), 'firmware_updates', ['chargepoint_id'], unique=False)
|
||||
op.alter_column('users', 'is_active',
|
||||
existing_type=sa.BOOLEAN(),
|
||||
nullable=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('users', 'is_active',
|
||||
existing_type=sa.BOOLEAN(),
|
||||
nullable=True)
|
||||
op.drop_index(op.f('ix_firmware_updates_chargepoint_id'), table_name='firmware_updates')
|
||||
op.drop_table('firmware_updates')
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,32 @@
|
|||
"""Add signature fields to firmware_update table
|
||||
|
||||
Revision ID: 506cc8d086c9
|
||||
Revises: 00edfb13e611
|
||||
Create Date: 2025-03-23 14:49:42.662564+00:00
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '506cc8d086c9'
|
||||
down_revision: Union[str, None] = '00edfb13e611'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('firmware_updates', sa.Column('signing_certificate', sa.Text(), nullable=True))
|
||||
op.add_column('firmware_updates', sa.Column('signature', sa.String(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('firmware_updates', 'signature')
|
||||
op.drop_column('firmware_updates', 'signing_certificate')
|
||||
# ### end Alembic commands ###
|
|
@ -10,10 +10,9 @@ from sqlalchemy import select
|
|||
from sqlalchemy.orm import Session
|
||||
from argon2 import PasswordHasher
|
||||
|
||||
from app.models import *
|
||||
|
||||
load_dotenv()
|
||||
|
||||
from app.models import *
|
||||
from app.database import SessionLocal
|
||||
|
||||
def __get_user_by_email(db: Session, email: str):
|
||||
|
|
|
@ -3,7 +3,10 @@ from sqlalchemy import create_engine
|
|||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
SQLALCHEMY_DATABASE_URL = os.getenv("CS_DATABASE_URL", "sqlite:///./simple-ocpp-cs.db")
|
||||
if os.getenv("CS_DATABASE_URL", "invalid") == "invalid":
|
||||
raise SystemExit('ERROR: Invalid CS_DATABASE_URL environment variable')
|
||||
|
||||
SQLALCHEMY_DATABASE_URL = os.getenv("CS_DATABASE_URL")
|
||||
|
||||
if SQLALCHEMY_DATABASE_URL.startswith("sqlite"):
|
||||
engine = create_engine(
|
||||
|
|
|
@ -28,6 +28,7 @@ def create_ocpp_app():
|
|||
def create_app():
|
||||
app = FastAPI(
|
||||
title="simple-ocpp-cs",
|
||||
version="0.2.0",
|
||||
summary="Simple implementation of a basic OCPP 2.0.1 compliant central system (backend) for EV charging stations",
|
||||
responses={404: {"description": "Not found"}},
|
||||
)
|
||||
|
|
|
@ -2,6 +2,7 @@ __all__ = [
|
|||
"chargepoint_variable",
|
||||
"chargepoint",
|
||||
"connector",
|
||||
"firmware_update",
|
||||
"id_token",
|
||||
"meter_value",
|
||||
"session",
|
||||
|
|
22
backend/app/models/firmware_update.py
Normal file
22
backend/app/models/firmware_update.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
import uuid
|
||||
from sqlalchemy import Column, DateTime, Enum, ForeignKey, Integer, String, Text, Uuid
|
||||
|
||||
from app.database import Base
|
||||
from app.schemas.firmware_update import FirmwareUpdateStatus
|
||||
|
||||
class FirmwareUpdate(Base):
|
||||
__tablename__ = "firmware_updates"
|
||||
|
||||
id = Column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
request_id = Column(Integer)
|
||||
status = Column(Enum(FirmwareUpdateStatus))
|
||||
|
||||
retries = Column(Integer)
|
||||
retry_interval = Column(Integer)
|
||||
location = Column(String)
|
||||
retrieve_date_time = Column(DateTime)
|
||||
install_date_time = Column(DateTime, nullable=True)
|
||||
signing_certificate = Column(Text, nullable=True)
|
||||
signature = Column(String, nullable=True)
|
||||
|
||||
chargepoint_id = Column(Uuid, ForeignKey("chargepoints.id"), index=True)
|
|
@ -8,7 +8,7 @@ class Session(Base):
|
|||
|
||||
id = Column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
name = Column(String)
|
||||
refresh_token = Column(String, unique=True, index=True)
|
||||
refresh_token = Column(String, nullable=False, unique=True, index=True)
|
||||
last_used = Column(DateTime(timezone=True))
|
||||
|
||||
user_id = Column(Uuid, ForeignKey("users.id"), index=True)
|
||||
user_id = Column(Uuid, ForeignKey("users.id"), nullable=False, index=True)
|
|
@ -8,6 +8,7 @@ from ocpp.v201.enums import Action, RegistrationStatusEnumType, TransactionEvent
|
|||
from ocpp.v201.call import GetBaseReport
|
||||
|
||||
from app.services import (
|
||||
firmware_service,
|
||||
variable_service,
|
||||
id_token_service,
|
||||
chargepoint_service,
|
||||
|
@ -108,6 +109,11 @@ class ChargePoint(cp):
|
|||
return call_result.TransactionEvent()
|
||||
else:
|
||||
return call_result.TransactionEvent(id_token_info=id_token_info)
|
||||
|
||||
@on(Action.firmware_status_notification)
|
||||
async def on_firmware_status_notification(self, status, request_id, **kwargs):
|
||||
await firmware_service.update_firmware_status(self.id, request_id, status)
|
||||
return call_result.FirmwareStatusNotification()
|
||||
|
||||
@on(Action.meter_values)
|
||||
async def on_meter_values(self, **kwargs):
|
||||
|
|
|
@ -45,7 +45,7 @@ async def logout(
|
|||
Remove the current session based on the access token, effectively invalidating the current refresh token.
|
||||
"""
|
||||
await session_service.remove_session(
|
||||
db=db, id=UUID(token.session), initiator=f"user:{token.subject}"
|
||||
db=db, id=UUID(token.session)
|
||||
)
|
||||
return list()
|
||||
|
||||
|
|
|
@ -28,10 +28,13 @@ from app.schemas.chargepoint_variable import (
|
|||
MutabilityType,
|
||||
SetVariableStatusType
|
||||
)
|
||||
from app.schemas.firmware_update import FirmwareUpdate, FirmwareUpdateCreate, FirmwareUpdateSubmissionResponse
|
||||
from app.models.chargepoint import ChargePoint as DbChargePoint
|
||||
from app.models.user import User as DbUser
|
||||
from app.models.chargepoint_variable import ChargepointVariable as DbChargepointVariable
|
||||
from app.models.firmware_update import FirmwareUpdate as DbFirmwareUpdate
|
||||
from app.security.jwt_bearer import JWTBearer
|
||||
from app.services import firmware_service
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/chargepoints",
|
||||
|
@ -293,3 +296,67 @@ async def update_chargepoint_variable(
|
|||
return ChargepointVariableResponse(status=status)
|
||||
except TimeoutError:
|
||||
raise HTTPException(status_code=503, detail="Chargepoint didn't respond in time.")
|
||||
|
||||
@router.get(path="/{chargepoint_id}/firmware-updates", response_model=list[FirmwareUpdate])
|
||||
async def get_firmware_updates(
|
||||
chargepoint_id: UUID,
|
||||
db: Session = Depends(get_db),
|
||||
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
|
||||
):
|
||||
chargepoint = db.get(DbChargePoint, chargepoint_id)
|
||||
if chargepoint is None:
|
||||
raise HTTPException(status_code=404, detail="Chargepoint not found")
|
||||
|
||||
firmware_updates = db.query(DbFirmwareUpdate).filter(
|
||||
DbFirmwareUpdate.chargepoint_id == chargepoint_id
|
||||
).all()
|
||||
|
||||
return firmware_updates
|
||||
|
||||
@router.get(path="/{chargepoint_id}/firmware-updates/{firmware_update_id}", response_model=FirmwareUpdate)
|
||||
async def get_firmware_update(
|
||||
chargepoint_id: UUID,
|
||||
firmware_update_id: UUID,
|
||||
db: Session = Depends(get_db),
|
||||
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
|
||||
):
|
||||
chargepoint = db.get(DbChargePoint, chargepoint_id)
|
||||
if chargepoint is None:
|
||||
raise HTTPException(status_code=404, detail="Chargepoint not found")
|
||||
|
||||
firmware_update = db.query(DbFirmwareUpdate).filter(
|
||||
DbFirmwareUpdate.chargepoint_id == chargepoint_id,
|
||||
DbFirmwareUpdate.id == firmware_update_id
|
||||
).first()
|
||||
if firmware_update is None:
|
||||
raise HTTPException(status_code=404, detail="FirmwareUpdate not found")
|
||||
|
||||
return firmware_update
|
||||
|
||||
@router.post(path="/{chargepoint_id}/firmware-updates", response_model=FirmwareUpdate)
|
||||
async def create_firmware_update(
|
||||
chargepoint_id: UUID,
|
||||
firmware_update: FirmwareUpdateCreate,
|
||||
db: Session = Depends(get_db),
|
||||
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
|
||||
):
|
||||
chargepoint = db.get(DbChargePoint, chargepoint_id)
|
||||
if chargepoint is None:
|
||||
raise HTTPException(status_code=404, detail="Chargepoint not found")
|
||||
|
||||
firmware_update = await firmware_service.create_firmware_update(chargepoint_id, firmware_update)
|
||||
return firmware_update
|
||||
|
||||
@router.post(path="/{chargepoint_id}/firmware-updates/{firmware_update_id}/submit", response_model=ChargePointResetResponse)
|
||||
async def submit_firmware_update(
|
||||
chargepoint_id: UUID,
|
||||
firmware_update_id: UUID,
|
||||
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
|
||||
):
|
||||
if chargepoint_manager.is_connected(chargepoint_id) == False:
|
||||
raise HTTPException(status_code=503, detail="Chargepoint not connected.")
|
||||
try:
|
||||
_, status = await firmware_service.submit_firmware_update(firmware_update_id)
|
||||
return FirmwareUpdateSubmissionResponse(status=status)
|
||||
except TimeoutError:
|
||||
raise HTTPException(status_code=503, detail="Chargepoint didn't respond in time.")
|
||||
|
|
|
@ -24,7 +24,7 @@ async def get_id_tokens(
|
|||
db: Session = Depends(get_db),
|
||||
token: AccessToken = Depends(JWTBearer()),
|
||||
):
|
||||
stmt = select(Session)
|
||||
stmt = select(DbIdToken)
|
||||
if token.role != Role.ADMINISTRATOR:
|
||||
stmt = stmt.where(DbIdToken.owner_id == token.subject)
|
||||
stmt = stmt.order_by(DbIdToken.id).offset(skip).limit(limit)
|
||||
|
@ -42,7 +42,7 @@ async def get_id_token(
|
|||
id_token = result.scalars().first()
|
||||
if id_token == None:
|
||||
raise HTTPException(status_code=404, detail="IdToken not found")
|
||||
if token.role != Role.ADMINISTRATOR & id_token.owner_id != token.subject:
|
||||
if token.role != Role.ADMINISTRATOR and id_token.owner_id != token.subject:
|
||||
raise HTTPException(status_code=404, detail="IdToken not found")
|
||||
return id_token
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ async def get_transaction(
|
|||
transaction = result.scalars().first()
|
||||
if transaction == None:
|
||||
raise HTTPException(404, "Transaction not found")
|
||||
if token.role != Role.ADMINISTRATOR & transaction.user_id != token.subject:
|
||||
if token.role != Role.ADMINISTRATOR and transaction.user_id != token.subject:
|
||||
raise HTTPException(404, "Transaction not found")
|
||||
return transaction
|
||||
|
||||
|
@ -59,7 +59,7 @@ async def get_transaction_meter_values(
|
|||
transaction = result.scalars().first()
|
||||
if transaction == None:
|
||||
raise HTTPException(404, "Transaction not found")
|
||||
if token.role != Role.ADMINISTRATOR & transaction.user_id != token.subject:
|
||||
if token.role != Role.ADMINISTRATOR and transaction.user_id != token.subject:
|
||||
raise HTTPException(404, "Transaction not found")
|
||||
stmt = select(DbMeterValue).where(DbMeterValue.transaction_id == transaction_id).order_by(DbMeterValue.timestamp)
|
||||
result = db.execute(stmt)
|
||||
|
@ -76,7 +76,7 @@ async def remote_stop_transaction(
|
|||
transaction = result.scalars().first()
|
||||
if transaction == None:
|
||||
raise HTTPException(404, "Transaction not found")
|
||||
if token.role != Role.ADMINISTRATOR & transaction.user_id != token.subject:
|
||||
if token.role != Role.ADMINISTRATOR and transaction.user_id != token.subject:
|
||||
raise HTTPException(404, "Transaction not found")
|
||||
if transaction.status != TransactionStatus.ONGOING:
|
||||
raise HTTPException(status_code=422, detail=[{
|
||||
|
|
43
backend/app/schemas/firmware_update.py
Normal file
43
backend/app/schemas/firmware_update.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
from datetime import datetime
|
||||
import enum
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
from pydantic import BaseModel
|
||||
|
||||
class FirmwareUpdateStatus(enum.Enum):
|
||||
CREATED = "xCreated"
|
||||
SUBMITTED = "xSubmitted"
|
||||
DOWNLOADED = "Downloaded"
|
||||
DOWNLOAD_FAILED = "DownloadFailed"
|
||||
DOWNLOADING = "Downloading"
|
||||
DOWNLOAD_SCHEDULED = "DownloadScheduled"
|
||||
DOWNLOAD_PAUSED = "DownloadPaused"
|
||||
IDLE = "Idle"
|
||||
INSTALLATION_FAILED = "InstallationFailed"
|
||||
INSTALLING = "Installing"
|
||||
INSTALLED = "Installed"
|
||||
INSTALL_REBOOTING = "InstallRebooting"
|
||||
INSTALL_SCHEDULED = "InstallScheduled"
|
||||
INSTALL_VERIFICATION_FAILED = "InstallVerificationFailed"
|
||||
INVALID_SIGNATURE = "InvalidSignature"
|
||||
SIGNATURE_VERIFIED = "SignatureVerified"
|
||||
|
||||
class FirmwareUpdateBase(BaseModel):
|
||||
retries: int
|
||||
retry_interval: int
|
||||
location: str
|
||||
retrieve_date_time: datetime
|
||||
install_date_time: Optional[datetime]
|
||||
signing_certificate: Optional[str]
|
||||
signature: Optional[str]
|
||||
|
||||
class FirmwareUpdate(FirmwareUpdateBase):
|
||||
id: UUID
|
||||
request_id: int
|
||||
status: FirmwareUpdateStatus
|
||||
|
||||
class FirmwareUpdateCreate(FirmwareUpdateBase):
|
||||
pass
|
||||
|
||||
class FirmwareUpdateSubmissionResponse(BaseModel):
|
||||
status: str
|
66
backend/app/services/firmware_service.py
Normal file
66
backend/app/services/firmware_service.py
Normal file
|
@ -0,0 +1,66 @@
|
|||
from uuid import UUID
|
||||
|
||||
from ocpp.v201.call import UpdateFirmware
|
||||
from ocpp.v201.call_result import UpdateFirmware as UpdateFirmwareResult
|
||||
from ocpp.v201.datatypes import FirmwareType
|
||||
|
||||
from app.database import SessionLocal
|
||||
from app.models.chargepoint import ChargePoint
|
||||
from app.models.firmware_update import FirmwareUpdate
|
||||
from app.ocpp_proto import chargepoint_manager
|
||||
from app.schemas.firmware_update import FirmwareUpdateCreate, FirmwareUpdateStatus
|
||||
|
||||
async def create_firmware_update(chargepoint_id: UUID, firmware_update: FirmwareUpdateCreate) -> FirmwareUpdate:
|
||||
with SessionLocal() as db:
|
||||
db_chargepoint = db.get(ChargePoint, chargepoint_id)
|
||||
latest_firmware_update = db.query(FirmwareUpdate).filter(FirmwareUpdate.chargepoint_id == db_chargepoint.id).order_by(FirmwareUpdate.request_id.desc()).first()
|
||||
new_request_id = latest_firmware_update.request_id + 1 if latest_firmware_update else 1
|
||||
db_firmware_update = FirmwareUpdate(
|
||||
request_id=new_request_id,
|
||||
status=FirmwareUpdateStatus.CREATED,
|
||||
retries=firmware_update.retries,
|
||||
retry_interval=firmware_update.retry_interval,
|
||||
location=firmware_update.location,
|
||||
retrieve_date_time=firmware_update.retrieve_date_time,
|
||||
install_date_time=firmware_update.install_date_time,
|
||||
chargepoint_id=db_chargepoint.id,
|
||||
signing_certificate=firmware_update.signing_certificate,
|
||||
signature=firmware_update.signature
|
||||
)
|
||||
db.add(db_firmware_update)
|
||||
db.commit()
|
||||
db.refresh(db_firmware_update)
|
||||
return db_firmware_update
|
||||
|
||||
async def submit_firmware_update(firmware_update_id: UUID) -> tuple[FirmwareUpdate, str]:
|
||||
with SessionLocal() as db:
|
||||
db_firmware_update = db.get(FirmwareUpdate, firmware_update_id)
|
||||
try:
|
||||
result: UpdateFirmwareResult = await chargepoint_manager.call(
|
||||
db_firmware_update.chargepoint_id,
|
||||
payload=UpdateFirmware(
|
||||
request_id=db_firmware_update.request_id,
|
||||
retries=db_firmware_update.retries,
|
||||
retry_interval=db_firmware_update.retry_interval,
|
||||
firmware=FirmwareType(
|
||||
location=db_firmware_update.location,
|
||||
retrieve_date_time=db_firmware_update.retrieve_date_time.isoformat(),
|
||||
install_date_time=db_firmware_update.install_date_time.isoformat(),
|
||||
signing_certificate=db_firmware_update.signing_certificate,
|
||||
signature=db_firmware_update.signature
|
||||
)
|
||||
))
|
||||
if result.status == "Accepted" or result.status == "AcceptedCanceled":
|
||||
db_firmware_update.status = FirmwareUpdateStatus.SUBMITTED
|
||||
db.commit()
|
||||
|
||||
return db_firmware_update, result.status
|
||||
except TimeoutError as e:
|
||||
raise e
|
||||
|
||||
async def update_firmware_status(chargepoint_identity: str, request_id: int, status: FirmwareUpdateStatus):
|
||||
with SessionLocal() as db:
|
||||
db_chargepoint = db.query(ChargePoint).filter(ChargePoint.identity == chargepoint_identity).first()
|
||||
db_firmware_update = db.query(FirmwareUpdate).filter(FirmwareUpdate.chargepoint_id == db_chargepoint.id).filter(FirmwareUpdate.request_id == request_id).first()
|
||||
db_firmware_update.status = FirmwareUpdateStatus(status)
|
||||
db.commit()
|
Loading…
Add table
Reference in a new issue