Compare commits

..

2 commits

Author SHA1 Message Date
f0eff338ff
Add possibility for signed firmware updates
All checks were successful
ci/woodpecker/push/docker Pipeline was successful
ci/woodpecker/cron/docker Pipeline was successful
2025-03-23 18:54:48 +00:00
b59aeeb5e5
Add firmware update logic 2025-03-23 18:54:36 +00:00
5 changed files with 47 additions and 8 deletions

View file

@ -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 ###

View file

@ -1,5 +1,5 @@
import uuid import uuid
from sqlalchemy import Column, DateTime, Enum, ForeignKey, Integer, String, Uuid from sqlalchemy import Column, DateTime, Enum, ForeignKey, Integer, String, Text, Uuid
from app.database import Base from app.database import Base
from app.schemas.firmware_update import FirmwareUpdateStatus from app.schemas.firmware_update import FirmwareUpdateStatus
@ -16,5 +16,7 @@ class FirmwareUpdate(Base):
location = Column(String) location = Column(String)
retrieve_date_time = Column(DateTime) retrieve_date_time = Column(DateTime)
install_date_time = Column(DateTime, nullable=True) 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) chargepoint_id = Column(Uuid, ForeignKey("chargepoints.id"), index=True)

View file

@ -356,7 +356,7 @@ async def submit_firmware_update(
if chargepoint_manager.is_connected(chargepoint_id) == False: if chargepoint_manager.is_connected(chargepoint_id) == False:
raise HTTPException(status_code=503, detail="Chargepoint not connected.") raise HTTPException(status_code=503, detail="Chargepoint not connected.")
try: try:
firmware_update, status = await firmware_service.submit_firmware_update(firmware_update_id) _, status = await firmware_service.submit_firmware_update(firmware_update_id)
return FirmwareUpdateSubmissionResponse(firmware_update, status) return FirmwareUpdateSubmissionResponse(status=status)
except TimeoutError: except TimeoutError:
raise HTTPException(status_code=503, detail="Chargepoint didn't respond in time.") raise HTTPException(status_code=503, detail="Chargepoint didn't respond in time.")

View file

@ -28,6 +28,8 @@ class FirmwareUpdateBase(BaseModel):
location: str location: str
retrieve_date_time: datetime retrieve_date_time: datetime
install_date_time: Optional[datetime] install_date_time: Optional[datetime]
signing_certificate: Optional[str]
signature: Optional[str]
class FirmwareUpdate(FirmwareUpdateBase): class FirmwareUpdate(FirmwareUpdateBase):
id: UUID id: UUID
@ -38,5 +40,4 @@ class FirmwareUpdateCreate(FirmwareUpdateBase):
pass pass
class FirmwareUpdateSubmissionResponse(BaseModel): class FirmwareUpdateSubmissionResponse(BaseModel):
firmware_update: FirmwareUpdate
status: str status: str

View file

@ -23,7 +23,9 @@ async def create_firmware_update(chargepoint_id: UUID, firmware_update: Firmware
location=firmware_update.location, location=firmware_update.location,
retrieve_date_time=firmware_update.retrieve_date_time, retrieve_date_time=firmware_update.retrieve_date_time,
install_date_time=firmware_update.install_date_time, install_date_time=firmware_update.install_date_time,
chargepoint_id=db_chargepoint.id chargepoint_id=db_chargepoint.id,
signing_certificate=firmware_update.signing_certificate,
signature=firmware_update.signature
) )
db.add(db_firmware_update) db.add(db_firmware_update)
db.commit() db.commit()
@ -42,8 +44,10 @@ async def submit_firmware_update(firmware_update_id: UUID) -> tuple[FirmwareUpda
retry_interval=db_firmware_update.retry_interval, retry_interval=db_firmware_update.retry_interval,
firmware=FirmwareType( firmware=FirmwareType(
location=db_firmware_update.location, location=db_firmware_update.location,
retrieve_date_time=db_firmware_update.retrieve_date_time, retrieve_date_time=db_firmware_update.retrieve_date_time.isoformat(),
install_date_time=db_firmware_update.install_date_time 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": if result.status == "Accepted" or result.status == "AcceptedCanceled":
@ -58,5 +62,5 @@ async def update_firmware_status(chargepoint_identity: str, request_id: int, sta
with SessionLocal() as db: with SessionLocal() as db:
db_chargepoint = db.query(ChargePoint).filter(ChargePoint.identity == chargepoint_identity).first() 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 = db.query(FirmwareUpdate).filter(FirmwareUpdate.chargepoint_id == db_chargepoint.id).filter(FirmwareUpdate.request_id == request_id).first()
db_firmware_update.status = status db_firmware_update.status = FirmwareUpdateStatus(status)
db.commit() db.commit()