`responsesFailed`: failed responses payload array (optional)
}
+###### Lock Connector
+
+- Request:
+ `ProcedureName`: 'lockConnector'
+ `PDU`: {
+ `hashIds`: charging station unique identifier strings array (optional, default: all charging stations),
+ `connectorId`: connector id integer
+ }
+
+- Response:
+ `PDU`: {
+ `status`: 'success' | 'failure',
+ `hashIdsSucceeded`: charging station unique identifier strings array,
+ `hashIdsFailed`: charging station unique identifier strings array (optional),
+ `responsesFailed`: failed responses payload array (optional)
+ }
+
+###### Unlock Connector
+
+- Request:
+ `ProcedureName`: 'unlockConnector'
+ `PDU`: {
+ `hashIds`: charging station unique identifier strings array (optional, default: all charging stations),
+ `connectorId`: connector id integer
+ }
+
+- Response:
+ `PDU`: {
+ `status`: 'success' | 'failure',
+ `hashIdsSucceeded`: charging station unique identifier strings array,
+ `hashIdsFailed`: charging station unique identifier strings array (optional),
+ `responsesFailed`: failed responses payload array (optional)
+ }
+
###### OCPP commands trigger
- Request:
return this.wsConnection?.readyState === WebSocket.OPEN
}
+ public lockConnector (connectorId: number): void {
+ if (connectorId === 0) {
+ logger.warn(`${this.logPrefix()} lockConnector: connector id 0 is not a physical connector`)
+ return
+ }
+ if (!this.hasConnector(connectorId)) {
+ logger.warn(
+ `${this.logPrefix()} lockConnector: connector id ${connectorId.toString()} does not exist`
+ )
+ return
+ }
+ const connectorStatus = this.getConnectorStatus(connectorId)
+ if (connectorStatus == null) {
+ logger.warn(
+ `${this.logPrefix()} lockConnector: connector id ${connectorId.toString()} status is null`
+ )
+ return
+ }
+ if (connectorStatus.locked !== true) {
+ connectorStatus.locked = true
+ this.emitChargingStationEvent(ChargingStationEvents.connectorStatusChanged, {
+ connectorId,
+ ...connectorStatus,
+ })
+ }
+ }
+
public logPrefix = (): string => {
if (
this instanceof ChargingStation &&
this.emitChargingStationEvent(ChargingStationEvents.updated)
}
+ public unlockConnector (connectorId: number): void {
+ if (connectorId === 0) {
+ logger.warn(`${this.logPrefix()} unlockConnector: connector id 0 is not a physical connector`)
+ return
+ }
+ if (!this.hasConnector(connectorId)) {
+ logger.warn(
+ `${this.logPrefix()} unlockConnector: connector id ${connectorId.toString()} does not exist`
+ )
+ return
+ }
+ const connectorStatus = this.getConnectorStatus(connectorId)
+ if (connectorStatus == null) {
+ logger.warn(
+ `${this.logPrefix()} unlockConnector: connector id ${connectorId.toString()} status is null`
+ )
+ return
+ }
+ if (connectorStatus.locked !== false) {
+ connectorStatus.locked = false
+ this.emitChargingStationEvent(ChargingStationEvents.connectorStatusChanged, {
+ connectorId,
+ ...connectorStatus,
+ })
+ }
+ }
+
private add (): void {
this.emitChargingStationEvent(ChargingStationEvents.added)
}
this.passthrough(RequestCommand.GET_CERTIFICATE_STATUS),
],
[BroadcastChannelProcedureName.HEARTBEAT, this.passthrough(RequestCommand.HEARTBEAT)],
+ [
+ BroadcastChannelProcedureName.LOCK_CONNECTOR,
+ (requestPayload?: BroadcastChannelRequestPayload) => {
+ if (requestPayload?.connectorId == null) {
+ throw new BaseError(
+ `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'connectorId' field is required`
+ )
+ }
+ this.chargingStation.lockConnector(requestPayload.connectorId)
+ },
+ ],
[
BroadcastChannelProcedureName.LOG_STATUS_NOTIFICATION,
this.passthrough(RequestCommand.LOG_STATUS_NOTIFICATION),
BroadcastChannelProcedureName.TRANSACTION_EVENT,
this.passthrough(RequestCommand.TRANSACTION_EVENT),
],
+ [
+ BroadcastChannelProcedureName.UNLOCK_CONNECTOR,
+ (requestPayload?: BroadcastChannelRequestPayload) => {
+ if (requestPayload?.connectorId == null) {
+ throw new BaseError(
+ `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'connectorId' field is required`
+ )
+ }
+ this.chargingStation.unlockConnector(requestPayload.connectorId)
+ },
+ ],
])
this.onmessage = this.requestHandler.bind(this) as (message: unknown) => void
this.onmessageerror = this.messageErrorHandler.bind(this) as (message: unknown) => void
connectorId,
status: OCPP16ChargePointStatus.Available,
} as OCPP16StatusNotificationRequest)
+ chargingStation.unlockConnector(connectorId)
return OCPP16Constants.OCPP_RESPONSE_UNLOCKED
}
connectorStatus.transactionId = payload.transactionId
connectorStatus.transactionIdTag = requestPayload.idTag
connectorStatus.transactionEnergyActiveImportRegisterValue = 0
+ connectorStatus.locked = true
connectorStatus.transactionBeginMeterValue =
OCPP16ServiceUtils.buildTransactionBeginMeterValue(
chargingStation,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
chargingStation.powerDivider!--
}
- resetConnectorStatus(chargingStation.getConnectorStatus(transactionConnectorId))
+ const transactionConnectorStatus = chargingStation.getConnectorStatus(transactionConnectorId)
+ resetConnectorStatus(transactionConnectorStatus)
+ if (
+ transactionConnectorStatus != null &&
+ (payload.idTagInfo == null || payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED)
+ ) {
+ transactionConnectorStatus.locked = false
+ }
OCPP16ServiceUtils.stopPeriodicMeterValues(chargingStation, transactionConnectorId)
const logMsg = `${chargingStation.logPrefix()} ${moduleName}.handleResponseStopTransaction: Transaction with id ${requestPayload.transactionId.toString()} STOPPED on ${
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
evseId,
} as unknown as OCPP20StatusNotificationRequest)
+ chargingStation.unlockConnector(connectorId)
return { status: UnlockStatusEnumType.Unlocked }
} catch (error) {
logger.error(
const isIdTokenAccepted =
payload.idTokenInfo == null ||
payload.idTokenInfo.status === OCPP20AuthorizationStatusEnumType.Accepted
+ if (isIdTokenAccepted) {
+ connectorStatus.locked = true
+ }
if (connectorId != null && isIdTokenAccepted) {
sendAndSetConnectorStatus(chargingStation, {
connectorId,
OCPP20ServiceUtils.stopPeriodicMeterValues(chargingStation, connectorId)
resetConnectorStatus(connectorStatus)
+ connectorStatus.locked = false
await sendAndSetConnectorStatus(chargingStation, {
connectorId,
connectorStatus: ConnectorStatusEnum.Available,
hashIds,
})
+const connectorInputSchema = z.object({
+ connectorId: z.number().int().positive().describe('Target connector ID'),
+ hashIds,
+})
+
const emptyInputSchema = z.object({})
const chargingStationOptionsSchema = z.object({
inputSchema: emptyInputSchema,
},
],
+ [
+ ProcedureName.LOCK_CONNECTOR,
+ {
+ description: 'Engage the cable retention lock on a connector',
+ inputSchema: connectorInputSchema,
+ },
+ ],
[
ProcedureName.LOG_STATUS_NOTIFICATION,
{
inputSchema: ocppInputSchema(ProcedureName.TRANSACTION_EVENT),
},
],
+ [
+ ProcedureName.UNLOCK_CONNECTOR,
+ {
+ description: 'Release the cable retention lock on a connector',
+ inputSchema: connectorInputSchema,
+ },
+ ],
])
],
[ProcedureName.GET_CERTIFICATE_STATUS, BroadcastChannelProcedureName.GET_CERTIFICATE_STATUS],
[ProcedureName.HEARTBEAT, BroadcastChannelProcedureName.HEARTBEAT],
+ [ProcedureName.LOCK_CONNECTOR, BroadcastChannelProcedureName.LOCK_CONNECTOR],
[ProcedureName.LOG_STATUS_NOTIFICATION, BroadcastChannelProcedureName.LOG_STATUS_NOTIFICATION],
[ProcedureName.METER_VALUES, BroadcastChannelProcedureName.METER_VALUES],
[
[ProcedureName.STOP_CHARGING_STATION, BroadcastChannelProcedureName.STOP_CHARGING_STATION],
[ProcedureName.STOP_TRANSACTION, BroadcastChannelProcedureName.STOP_TRANSACTION],
[ProcedureName.TRANSACTION_EVENT, BroadcastChannelProcedureName.TRANSACTION_EVENT],
+ [ProcedureName.UNLOCK_CONNECTOR, BroadcastChannelProcedureName.UNLOCK_CONNECTOR],
])
protected readonly requestHandlers: Map<ProcedureName, ProtocolRequestHandler>
idTagAuthorized?: boolean
idTagLocalAuthorized?: boolean
localAuthorizeIdTag?: string
+ locked?: boolean
MeterValues: SampledValueTemplate[]
remoteStartId?: number
reservation?: Reservation
HEARTBEAT = 'heartbeat',
LIST_CHARGING_STATIONS = 'listChargingStations',
LIST_TEMPLATES = 'listTemplates',
+ LOCK_CONNECTOR = 'lockConnector',
LOG_STATUS_NOTIFICATION = 'logStatusNotification',
METER_VALUES = 'meterValues',
NOTIFY_CUSTOMER_INFORMATION = 'notifyCustomerInformation',
STOP_SIMULATOR = 'stopSimulator',
STOP_TRANSACTION = 'stopTransaction',
TRANSACTION_EVENT = 'transactionEvent',
+ UNLOCK_CONNECTOR = 'unlockConnector',
}
export enum Protocol {
GET_15118_EV_CERTIFICATE = 'get15118EVCertificate',
GET_CERTIFICATE_STATUS = 'getCertificateStatus',
HEARTBEAT = 'heartbeat',
+ LOCK_CONNECTOR = 'lockConnector',
LOG_STATUS_NOTIFICATION = 'logStatusNotification',
METER_VALUES = 'meterValues',
NOTIFY_CUSTOMER_INFORMATION = 'notifyCustomerInformation',
STOP_CHARGING_STATION = 'stopChargingStation',
STOP_TRANSACTION = 'stopTransaction',
TRANSACTION_EVENT = 'transactionEvent',
+ UNLOCK_CONNECTOR = 'unlockConnector',
}
export type BroadcastChannelRequest = [
import type { ChargingStation } from '../../src/charging-station/index.js'
+import { resetConnectorStatus } from '../../src/charging-station/Helpers.js'
import { RegistrationStatusEnumType } from '../../src/types/index.js'
import { standardCleanup } from '../helpers/TestLifecycleHelpers.js'
import { TEST_ONE_HOUR_MS } from './ChargingStationTestConstants.js'
assert.strictEqual(found2?.connectorId, 2)
})
})
+
+ await describe('Connector Lock/Unlock', async () => {
+ let station: ChargingStation | undefined
+
+ beforeEach(() => {
+ station = undefined
+ })
+
+ afterEach(() => {
+ standardCleanup()
+ if (station != null) {
+ cleanupChargingStation(station)
+ }
+ })
+
+ await it('should set locked=true on lockConnector() for valid connector', () => {
+ const result = createMockChargingStation({ connectorsCount: 2 })
+ station = result.station
+
+ station.lockConnector(1)
+
+ assert.strictEqual(station.getConnectorStatus(1)?.locked, true)
+ })
+
+ await it('should set locked=false on unlockConnector() for valid connector', () => {
+ const result = createMockChargingStation({ connectorsCount: 2 })
+ station = result.station
+
+ station.lockConnector(1)
+ assert.strictEqual(station.getConnectorStatus(1)?.locked, true)
+
+ station.unlockConnector(1)
+ assert.strictEqual(station.getConnectorStatus(1)?.locked, false)
+ })
+
+ await it('should be idempotent on double lockConnector()', () => {
+ const result = createMockChargingStation({ connectorsCount: 2 })
+ station = result.station
+
+ station.lockConnector(1)
+ station.lockConnector(1)
+
+ assert.strictEqual(station.getConnectorStatus(1)?.locked, true)
+ })
+
+ await it('should be idempotent on double unlockConnector()', () => {
+ const result = createMockChargingStation({ connectorsCount: 2 })
+ station = result.station
+
+ station.unlockConnector(1)
+ station.unlockConnector(1)
+
+ assert.strictEqual(station.getConnectorStatus(1)?.locked, false)
+ })
+
+ await it('should reject connector id 0 for lockConnector()', () => {
+ const result = createMockChargingStation({ connectorsCount: 2 })
+ station = result.station
+
+ station.lockConnector(0)
+
+ assert.notStrictEqual(station.getConnectorStatus(0)?.locked, true)
+ })
+
+ await it('should reject connector id 0 for unlockConnector()', () => {
+ const result = createMockChargingStation({ connectorsCount: 2 })
+ station = result.station
+
+ station.unlockConnector(0)
+
+ assert.notStrictEqual(station.getConnectorStatus(0)?.locked, false)
+ })
+
+ await it('should reject non-existent connector for lockConnector()', () => {
+ const result = createMockChargingStation({ connectorsCount: 2 })
+ station = result.station
+
+ station.lockConnector(999)
+
+ assert.strictEqual(station.getConnectorStatus(999), undefined)
+ })
+
+ await it('should reject non-existent connector for unlockConnector()', () => {
+ const result = createMockChargingStation({ connectorsCount: 2 })
+ station = result.station
+
+ station.unlockConnector(999)
+
+ assert.strictEqual(station.getConnectorStatus(999), undefined)
+ })
+
+ await it('should not clear locked state on resetConnectorStatus', () => {
+ const result = createMockChargingStation({ connectorsCount: 2 })
+ station = result.station
+
+ station.lockConnector(1)
+ assert.strictEqual(station.getConnectorStatus(1)?.locked, true)
+
+ resetConnectorStatus(station.getConnectorStatus(1))
+
+ assert.strictEqual(station.getConnectorStatus(1)?.locked, true)
+ })
+ })
})
listenerCount: () => 0,
+ lockConnector (connectorId: number): void {
+ if (connectorId === 0) {
+ return
+ }
+ if (!this.hasConnector(connectorId)) {
+ return
+ }
+ const connectorStatus = this.getConnectorStatus(connectorId)
+ if (connectorStatus == null) {
+ return
+ }
+ connectorStatus.locked = true
+ },
+
logPrefix (): string {
return `${this.stationInfo.chargingStationId} |`
},
stopping: false,
templateFile,
+
+ unlockConnector (connectorId: number): void {
+ if (connectorId === 0) {
+ return
+ }
+ if (!this.hasConnector(connectorId)) {
+ return
+ }
+ const connectorStatus = this.getConnectorStatus(connectorId)
+ if (connectorStatus == null) {
+ return
+ }
+ connectorStatus.locked = false
+ },
+
wsConnection: null as MockWebSocket | null,
wsConnectionRetryCount: 0,
}
<td class="connectors-table__column">
{{ connector.status ?? 'Ø' }}
</td>
+ <td class="connectors-table__column">
+ {{ connector.locked === true ? 'Yes' : 'No' }}
+ </td>
<td class="connectors-table__column">
{{ connector.transactionStarted === true ? `Yes (${connector.transactionId})` : 'No' }}
</td>
<Button @click="stopAutomaticTransactionGenerator()">
Stop ATG
</Button>
+ <Button
+ v-if="connector.locked !== true"
+ @click="lockConnector()"
+ >
+ Lock
+ </Button>
+ <Button
+ v-else
+ @click="unlockConnector()"
+ >
+ Unlock
+ </Button>
</td>
</tr>
</template>
console.error('Error at stopping transaction:', error)
})
}
+const lockConnector = (): void => {
+ uiClient
+ .lockConnector(props.hashId, props.connectorId)
+ .then(() => {
+ return $toast.success('Connector successfully locked')
+ })
+ .catch((error: Error) => {
+ $toast.error('Error at locking connector')
+ console.error('Error at locking connector:', error)
+ })
+}
+const unlockConnector = (): void => {
+ uiClient
+ .unlockConnector(props.hashId, props.connectorId)
+ .then(() => {
+ return $toast.success('Connector successfully unlocked')
+ })
+ .catch((error: Error) => {
+ $toast.error('Error at unlocking connector')
+ console.error('Error at unlocking connector:', error)
+ })
+}
const startAutomaticTransactionGenerator = (): void => {
uiClient
.startAutomaticTransactionGenerator(props.hashId, props.connectorId)
>
Status
</th>
+ <th
+ class="connectors-table__column"
+ scope="col"
+ >
+ Locked
+ </th>
<th
class="connectors-table__column"
scope="col"
return this.sendRequest(ProcedureName.LIST_TEMPLATES, {})
}
+ public async lockConnector (hashId: string, connectorId: number): Promise<ResponsePayload> {
+ return this.sendRequest(ProcedureName.LOCK_CONNECTOR, {
+ connectorId,
+ hashIds: [hashId],
+ })
+ }
+
public async openConnection (hashId: string): Promise<ResponsePayload> {
return this.sendRequest(ProcedureName.OPEN_CONNECTION, {
hashIds: [hashId],
})
}
+ public async unlockConnector (hashId: string, connectorId: number): Promise<ResponsePayload> {
+ return this.sendRequest(ProcedureName.UNLOCK_CONNECTOR, {
+ connectorId,
+ hashIds: [hashId],
+ })
+ }
+
public unregisterWSEventListener<K extends keyof WebSocketEventMap>(
event: K,
listener: (event: WebSocketEventMap[K]) => void,
idTagAuthorized?: boolean
idTagLocalAuthorized?: boolean
localAuthorizeIdTag?: string
+ locked?: boolean
status?: ChargePointStatus
transactionEnergyActiveImportRegisterValue?: number // In Wh
/**
GET_CERTIFICATE_STATUS = 'getCertificateStatus',
LIST_CHARGING_STATIONS = 'listChargingStations',
LIST_TEMPLATES = 'listTemplates',
+ LOCK_CONNECTOR = 'lockConnector',
LOG_STATUS_NOTIFICATION = 'logStatusNotification',
NOTIFY_CUSTOMER_INFORMATION = 'notifyCustomerInformation',
NOTIFY_REPORT = 'notifyReport',
STOP_SIMULATOR = 'stopSimulator',
STOP_TRANSACTION = 'stopTransaction',
TRANSACTION_EVENT = 'transactionEvent',
+ UNLOCK_CONNECTOR = 'unlockConnector',
}
export enum Protocol {
it('should display No when transaction not started', () => {
const wrapper = mountCSConnector()
const cells = wrapper.findAll('td')
- expect(cells[2].text()).toBe('No')
+ expect(cells[3].text()).toBe('No')
})
it('should display Yes with transaction ID when transaction started', () => {
connector: createConnectorStatus({ transactionId: 12345, transactionStarted: true }),
})
const cells = wrapper.findAll('td')
- expect(cells[2].text()).toBe('Yes (12345)')
+ expect(cells[3].text()).toBe('Yes (12345)')
})
it('should display ATG started as Yes when active', () => {
const wrapper = mountCSConnector({ atgStatus: { start: true } })
const cells = wrapper.findAll('td')
- expect(cells[3].text()).toBe('Yes')
+ expect(cells[4].text()).toBe('Yes')
})
it('should display ATG started as No when not active', () => {
const wrapper = mountCSConnector({ atgStatus: { start: false } })
const cells = wrapper.findAll('td')
- expect(cells[3].text()).toBe('No')
+ expect(cells[4].text()).toBe('No')
})
it('should display ATG started as No when atgStatus undefined', () => {
const wrapper = mountCSConnector()
const cells = wrapper.findAll('td')
- expect(cells[3].text()).toBe('No')
+ expect(cells[4].text()).toBe('No')
})
})
)
})
})
+
+ describe('lock/unlock actions', () => {
+ it('should display Locked column as No when not locked', () => {
+ const wrapper = mountCSConnector()
+ const cells = wrapper.findAll('td')
+ expect(cells[2].text()).toBe('No')
+ })
+
+ it('should display Locked column as Yes when locked', () => {
+ const wrapper = mountCSConnector({
+ connector: createConnectorStatus({ locked: true }),
+ })
+ const cells = wrapper.findAll('td')
+ expect(cells[2].text()).toBe('Yes')
+ })
+
+ it('should show Lock button when connector is not locked', () => {
+ const wrapper = mountCSConnector()
+ const buttons = wrapper.findAll('button')
+ const lockBtn = buttons.find(b => b.text() === 'Lock')
+ expect(lockBtn).toBeDefined()
+ expect(buttons.find(b => b.text() === 'Unlock')).toBeUndefined()
+ })
+
+ it('should show Unlock button when connector is locked', () => {
+ const wrapper = mountCSConnector({
+ connector: createConnectorStatus({ locked: true }),
+ })
+ const buttons = wrapper.findAll('button')
+ const unlockBtn = buttons.find(b => b.text() === 'Unlock')
+ expect(unlockBtn).toBeDefined()
+ expect(buttons.find(b => b.text() === 'Lock')).toBeUndefined()
+ })
+
+ it('should call lockConnector with correct params', async () => {
+ const wrapper = mountCSConnector()
+ const buttons = wrapper.findAll('button')
+ const lockBtn = buttons.find(b => b.text() === 'Lock')
+ await lockBtn?.trigger('click')
+ await flushPromises()
+ expect(mockClient.lockConnector).toHaveBeenCalledWith(TEST_HASH_ID, 1)
+ })
+
+ it('should call unlockConnector with correct params', async () => {
+ const wrapper = mountCSConnector({
+ connector: createConnectorStatus({ locked: true }),
+ })
+ const buttons = wrapper.findAll('button')
+ const unlockBtn = buttons.find(b => b.text() === 'Unlock')
+ await unlockBtn?.trigger('click')
+ await flushPromises()
+ expect(mockClient.unlockConnector).toHaveBeenCalledWith(TEST_HASH_ID, 1)
+ })
+
+ it('should show success toast after locking connector', async () => {
+ const wrapper = mountCSConnector()
+ const buttons = wrapper.findAll('button')
+ const lockBtn = buttons.find(b => b.text() === 'Lock')
+ await lockBtn?.trigger('click')
+ await flushPromises()
+ expect(toastMock.success).toHaveBeenCalledWith('Connector successfully locked')
+ })
+
+ it('should show error toast on lock failure', async () => {
+ mockClient.lockConnector.mockRejectedValueOnce(new Error('fail'))
+ const wrapper = mountCSConnector()
+ const buttons = wrapper.findAll('button')
+ const lockBtn = buttons.find(b => b.text() === 'Lock')
+ await lockBtn?.trigger('click')
+ await flushPromises()
+ expect(toastMock.error).toHaveBeenCalledWith('Error at locking connector')
+ })
+
+ it('should show success toast after unlocking connector', async () => {
+ const wrapper = mountCSConnector({
+ connector: createConnectorStatus({ locked: true }),
+ })
+ const buttons = wrapper.findAll('button')
+ const unlockBtn = buttons.find(b => b.text() === 'Unlock')
+ await unlockBtn?.trigger('click')
+ await flushPromises()
+ expect(toastMock.success).toHaveBeenCalledWith('Connector successfully unlocked')
+ })
+
+ it('should show error toast on unlock failure', async () => {
+ mockClient.unlockConnector.mockRejectedValueOnce(new Error('fail'))
+ const wrapper = mountCSConnector({
+ connector: createConnectorStatus({ locked: true }),
+ })
+ const buttons = wrapper.findAll('button')
+ const unlockBtn = buttons.find(b => b.text() === 'Unlock')
+ await unlockBtn?.trigger('click')
+ await flushPromises()
+ expect(toastMock.error).toHaveBeenCalledWith('Error at unlocking connector')
+ })
+ })
})
})
})
})
+
+ describe('connector lock operations', () => {
+ let client: UIClient
+ let sendRequestSpy: ReturnType<typeof vi.spyOn>
+
+ beforeEach(() => {
+ client = UIClient.getInstance(createUIServerConfig())
+ // @ts-expect-error — accessing private method for testing
+ sendRequestSpy = vi.spyOn(client, 'sendRequest').mockResolvedValue({
+ status: ResponseStatus.SUCCESS,
+ })
+ })
+
+ it('should send LOCK_CONNECTOR with hashIds and connectorId', async () => {
+ await client.lockConnector(TEST_HASH_ID, 1)
+ expect(sendRequestSpy).toHaveBeenCalledWith(ProcedureName.LOCK_CONNECTOR, {
+ connectorId: 1,
+ hashIds: [TEST_HASH_ID],
+ })
+ })
+
+ it('should send UNLOCK_CONNECTOR with hashIds and connectorId', async () => {
+ await client.unlockConnector(TEST_HASH_ID, 2)
+ expect(sendRequestSpy).toHaveBeenCalledWith(ProcedureName.UNLOCK_CONNECTOR, {
+ connectorId: 2,
+ hashIds: [TEST_HASH_ID],
+ })
+ })
+ })
})
deleteChargingStation: ReturnType<typeof vi.fn>
listChargingStations: ReturnType<typeof vi.fn>
listTemplates: ReturnType<typeof vi.fn>
+ lockConnector: ReturnType<typeof vi.fn>
openConnection: ReturnType<typeof vi.fn>
registerWSEventListener: ReturnType<typeof vi.fn>
setConfiguration: ReturnType<typeof vi.fn>
stopChargingStation: ReturnType<typeof vi.fn>
stopSimulator: ReturnType<typeof vi.fn>
stopTransaction: ReturnType<typeof vi.fn>
+ unlockConnector: ReturnType<typeof vi.fn>
unregisterWSEventListener: ReturnType<typeof vi.fn>
}
deleteChargingStation: vi.fn().mockResolvedValue(successResponse),
listChargingStations: vi.fn().mockResolvedValue({ ...successResponse, chargingStations: [] }),
listTemplates: vi.fn().mockResolvedValue({ ...successResponse, templates: [] }),
+ lockConnector: vi.fn().mockResolvedValue(successResponse),
openConnection: vi.fn().mockResolvedValue(successResponse),
registerWSEventListener: vi.fn(),
setConfiguration: vi.fn(),
stopChargingStation: vi.fn().mockResolvedValue(successResponse),
stopSimulator: vi.fn().mockResolvedValue(successResponse),
stopTransaction: vi.fn().mockResolvedValue(successResponse),
+ unlockConnector: vi.fn().mockResolvedValue(successResponse),
unregisterWSEventListener: vi.fn(),
}
}