Constants,
convertToDate,
formatDurationMilliSeconds,
+ isEmpty,
isValidDate,
logger,
logPrefix,
private startConnectors (stopAbsoluteDuration?: boolean): void {
if (
- this.connectorsStatus.size > 0 &&
+ !isEmpty(this.connectorsStatus) &&
this.connectorsStatus.size !== this.chargingStation.getNumberOfConnectors()
) {
this.connectorsStatus.clear()
public wsConnection: null | WebSocket
public get hasEvses (): boolean {
- return isEmpty(this.connectors) && this.evses.size > 0
+ return isEmpty(this.connectors) && !isEmpty(this.evses)
}
public get wsConnectionUrl (): URL {
}
private flushMessageBuffer (): void {
- if (!this.flushingMessageBuffer && this.messageQueue.length > 0) {
+ if (!this.flushingMessageBuffer && isNotEmptyArray<string>(this.messageQueue)) {
this.flushingMessageBuffer = true
this.sendMessageBuffer(() => {
this.flushingMessageBuffer = false
private initializeConnectorsOrEvsesFromFile (configuration: ChargingStationConfiguration): void {
if (configuration.connectorsStatus != null && configuration.evsesStatus == null) {
const isTupleFormat =
- configuration.connectorsStatus.length > 0 &&
+ isNotEmptyArray(configuration.connectorsStatus) &&
Array.isArray(configuration.connectorsStatus[0])
const entries: [number, ConnectorStatus][] = isTupleFormat
? (configuration.connectorsStatus as [number, ConnectorStatus][])
}
} else if (configuration.evsesStatus != null && configuration.connectorsStatus == null) {
const isTupleFormat =
- configuration.evsesStatus.length > 0 && Array.isArray(configuration.evsesStatus[0])
+ isNotEmptyArray(configuration.evsesStatus) && Array.isArray(configuration.evsesStatus[0])
const evseEntries: [number, EvseStatusConfiguration][] = isTupleFormat
? (configuration.evsesStatus as [number, EvseStatusConfiguration][])
: (configuration.evsesStatus as EvseStatusConfiguration[]).map((status, index) => [
delete evseStatus.connectorsStatus
const connIsTupleFormat =
evseStatusConfiguration.connectorsStatus != null &&
- evseStatusConfiguration.connectorsStatus.length > 0 &&
+ isNotEmptyArray(evseStatusConfiguration.connectorsStatus) &&
Array.isArray(evseStatusConfiguration.connectorsStatus[0])
const connEntries: [number, ConnectorStatus][] = connIsTupleFormat
? (evseStatusConfiguration.connectorsStatus as [number, ConnectorStatus][])
if (this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration !== true) {
delete configurationData.automaticTransactionGenerator
}
- if (this.connectors.size > 0) {
+ if (!isEmpty(this.connectors)) {
configurationData.connectorsStatus = buildConnectorsStatus(this)
} else {
delete configurationData.connectorsStatus
}
- if (this.evses.size > 0) {
+ if (!isEmpty(this.evses)) {
configurationData.evsesStatus = buildEvsesStatus(this)
} else {
delete configurationData.evsesStatus
automaticTransactionGenerator: configurationData.automaticTransactionGenerator,
configurationKey: configurationData.configurationKey,
stationInfo: configurationData.stationInfo,
- ...(this.connectors.size > 0 && {
+ ...(!isEmpty(this.connectors) && {
connectorsStatus: configurationData.connectorsStatus,
}),
- ...(this.evses.size > 0 && {
+ ...(!isEmpty(this.evses) && {
evsesStatus: configurationData.evsesStatus,
}),
} satisfies ChargingStationConfiguration),
onCompleteCallback: () => void,
messageIdx?: number
): void => {
- if (this.messageQueue.length > 0) {
+ if (isNotEmptyArray<string>(this.messageQueue)) {
const message = this.messageQueue[0]
let beginId: string | undefined
let commandName: RequestCommand | undefined
if (evses == null) {
return -1
}
- return Object.keys(evses).length
+ return isEmpty(evses) ? 0 : Object.keys(evses).length
}
const getMaxNumberOfConnectors = (
if (connectors == null) {
return -1
}
- return Object.keys(connectors).length
+ return isEmpty(connectors) ? 0 : Object.keys(connectors).length
}
export const getBootConnectorStatus = (
OCPP20ComponentName,
type OCPP20ConnectorStatusEnumType,
type OCPP20EVSEType,
+ type OCPP20GetVariableResultType,
OCPP20IdTokenEnumType,
type OCPP20IdTokenInfoType,
type OCPP20IdTokenType,
variable: { name: variableName },
},
])
- if (results.length > 0 && results[0].attributeValue != null) {
+ if (
+ isNotEmptyArray<OCPP20GetVariableResultType>(results) &&
+ results[0].attributeValue != null
+ ) {
return results[0].attributeValue
}
return undefined
const SOC_MAXIMUM_VALUE = 100
const UNIT_DIVIDER_KILO = 1000
-const MILLISECONDS_PER_HOUR = 3_600_000
+const MS_PER_HOUR = 3_600_000
export type Ajv = _Ajv.default
// eslint-disable-next-line @typescript-eslint/no-redeclare
const socMinimumValue = socSampledValueTemplate.minimumValue ?? 0
const socSampledValueTemplateValue = isNotEmptyString(socSampledValueTemplate.value)
? getRandomFloatFluctuatedRounded(
- Number.parseInt(socSampledValueTemplate.value),
+ convertToInt(socSampledValueTemplate.value),
socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
)
: randomInt(socMinimumValue, socMaximumValue + 1)
}
const voltageSampledValueTemplateValue = isNotEmptyString(voltageSampledValueTemplate.value)
- ? Number.parseInt(voltageSampledValueTemplate.value)
+ ? convertToInt(voltageSampledValueTemplate.value)
: chargingStation.getVoltageOut()
const fluctuationPercent =
voltageSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
let phaseMeasurandValue: number | undefined
if (phaseSampledValueTemplate != null) {
const templateValue = isNotEmptyString(phaseSampledValueTemplate.value)
- ? Number.parseInt(phaseSampledValueTemplate.value)
+ ? convertToInt(phaseSampledValueTemplate.value)
: nominalVoltage
phaseMeasurandValue = getRandomFloatFluctuatedRounded(
templateValue,
const connectorMaximumAvailablePower =
chargingStation.getConnectorMaximumAvailablePower(connectorId)
const connectorMaximumEnergyRounded = roundTo(
- (connectorMaximumAvailablePower * interval) / MILLISECONDS_PER_HOUR,
+ (connectorMaximumAvailablePower * interval) / MS_PER_HOUR,
2
)
const connectorMinimumEnergyRounded = roundTo(energyTemplate.minimumValue ?? 0, 2)
const connectorMaximumAvailablePower =
chargingStation.getConnectorMaximumAvailablePower(connectorId)
const connectorMaximumEnergyRounded = roundTo(
- (connectorMaximumAvailablePower * interval) / MILLISECONDS_PER_HOUR,
+ (connectorMaximumAvailablePower * interval) / MS_PER_HOUR,
2
)
const connectorMinimumEnergyRounded = roundTo(energyMeasurand.template.minimumValue ?? 0, 2)
import type { AuthCache, CacheStats } from '../interfaces/OCPPAuthService.js'
import type { AuthorizationResult } from '../types/AuthTypes.js'
-import { Constants, logger, roundTo, truncateId } from '../../../../utils/index.js'
+import { Constants, isEmpty, logger, roundTo, truncateId } from '../../../../utils/index.js'
import { AuthorizationStatus } from '../types/AuthTypes.js'
const moduleName = 'InMemoryAuthCache'
* Evict least recently used entry
*/
private evictLRU (): void {
- if (this.lruOrder.size === 0) {
+ if (isEmpty(this.lruOrder)) {
return
}
import {
createRateLimiter,
DEFAULT_RATE_LIMIT,
- DEFAULT_RATE_WINDOW,
+ DEFAULT_RATE_WINDOW_MS,
isValidCredential,
} from './UIServerSecurity.js'
import { getUsernameAndPasswordFromAuthorizationToken } from './UIServerUtils.js'
)
}
this.responseHandlers = new Map<UUIDv4, ServerResponse | WebSocket>()
- this.rateLimiter = createRateLimiter(DEFAULT_RATE_LIMIT, DEFAULT_RATE_WINDOW)
+ this.rateLimiter = createRateLimiter(DEFAULT_RATE_LIMIT, DEFAULT_RATE_WINDOW_MS)
this.uiServices = new Map<ProtocolVersion, AbstractUIService>()
}
import { AbstractUIServer } from './AbstractUIServer.js'
import {
createBodySizeLimiter,
- DEFAULT_COMPRESSION_THRESHOLD,
- DEFAULT_MAX_PAYLOAD_SIZE,
+ DEFAULT_COMPRESSION_THRESHOLD_BYTES,
+ DEFAULT_MAX_PAYLOAD_SIZE_BYTES,
} from './UIServerSecurity.js'
import { HttpMethod, isProtocolAndVersionSupported } from './UIServerUtils.js'
const body = JSONStringify(payload, undefined, MapStringifyFormat.object)
const shouldCompress =
this.acceptsGzip.get(uuid) === true &&
- Buffer.byteLength(body) >= DEFAULT_COMPRESSION_THRESHOLD
+ Buffer.byteLength(body) >= DEFAULT_COMPRESSION_THRESHOLD_BYTES
if (shouldCompress) {
res.writeHead(this.responseStatusToStatusCode(payload.status), {
}
const bodyBuffer: Uint8Array[] = []
- const checkBodySize = createBodySizeLimiter(DEFAULT_MAX_PAYLOAD_SIZE)
+ const checkBodySize = createBodySizeLimiter(DEFAULT_MAX_PAYLOAD_SIZE_BYTES)
req
.on('data', (chunk: Uint8Array) => {
if (!checkBodySize(chunk.length)) {
type UIServerConfiguration,
type UUIDv4,
} from '../../types/index.js'
-import { generateUUID, getErrorMessage, isNotEmptyArray, logger } from '../../utils/index.js'
+import {
+ generateUUID,
+ getErrorMessage,
+ isEmpty,
+ isNotEmptyArray,
+ logger,
+} from '../../utils/index.js'
import { AbstractUIServer } from './AbstractUIServer.js'
import {
mcpToolSchemas,
registerMCPResources,
registerMCPSchemaResources,
} from './mcp/index.js'
-import { DEFAULT_MAX_PAYLOAD_SIZE } from './UIServerSecurity.js'
+import { DEFAULT_MAX_PAYLOAD_SIZE_BYTES } from './UIServerSecurity.js'
import { HttpMethod } from './UIServerUtils.js'
const moduleName = 'UIMCPServer'
}
private injectOcppJsonSchemas (mcpServer: McpServer): void {
- if (this.ocppSchemaCache.size === 0) {
+ if (isEmpty(this.ocppSchemaCache)) {
return
}
// Access MCP SDK internal handler map — pinned to @modelcontextprotocol/sdk@~1.29.x
cache.set(procedureName, entry)
}
}
- if (cache.size > 0) {
+ if (!isEmpty(cache)) {
logger.info(
`${this.logPrefix(moduleName, 'loadOcppSchemas')} OCPP JSON schema injection enabled for ${cache.size.toString()} tool(s)`
)
let received = 0
for await (const chunk of req) {
received += (chunk as Buffer).length
- if (received > DEFAULT_MAX_PAYLOAD_SIZE) {
+ if (received > DEFAULT_MAX_PAYLOAD_SIZE_BYTES) {
throw new BaseError('Payload too large')
}
chunks.push(chunk as Buffer)
resetTime: number
}
-export const DEFAULT_MAX_PAYLOAD_SIZE = 1048576
+export const DEFAULT_MAX_PAYLOAD_SIZE_BYTES = 1048576
export const DEFAULT_RATE_LIMIT = 100
-export const DEFAULT_RATE_WINDOW = 60000
+export const DEFAULT_RATE_WINDOW_MS = 60000
export const DEFAULT_MAX_STATIONS = 100
export const DEFAULT_MAX_TRACKED_IPS = 10000
-export const DEFAULT_COMPRESSION_THRESHOLD = 1024
+export const DEFAULT_COMPRESSION_THRESHOLD_BYTES = 1024
export const isValidCredential = (provided: string, expected: string): boolean => {
try {
validateUUID,
} from '../../utils/index.js'
import { AbstractUIServer } from './AbstractUIServer.js'
-import { DEFAULT_COMPRESSION_THRESHOLD, DEFAULT_MAX_PAYLOAD_SIZE } from './UIServerSecurity.js'
+import {
+ DEFAULT_COMPRESSION_THRESHOLD_BYTES,
+ DEFAULT_MAX_PAYLOAD_SIZE_BYTES,
+} from './UIServerSecurity.js'
import {
getProtocolAndVersion,
handleProtocols,
super(uiServerConfiguration, bootstrap)
this.webSocketServer = new WebSocketServer({
handleProtocols,
- maxPayload: DEFAULT_MAX_PAYLOAD_SIZE,
+ maxPayload: DEFAULT_MAX_PAYLOAD_SIZE_BYTES,
noServer: true,
perMessageDeflate: {
clientNoContextTakeover: true,
concurrencyLimit: 10,
serverMaxWindowBits: 12,
serverNoContextTakeover: true,
- threshold: DEFAULT_COMPRESSION_THRESHOLD,
+ threshold: DEFAULT_COMPRESSION_THRESHOLD_BYTES,
zlibDeflateOptions: {
chunkSize: 16 * 1024,
level: 6,
/* This is intentional */
})
-export const workerSetVersion = '1.0.1'
+export const WORKER_SET_VERSION = '1.0.1'
export const DEFAULT_ELEMENT_ADD_DELAY_MS = 0
export const DEFAULT_WORKER_START_DELAY_MS = 500
import { SHARE_ENV, Worker } from 'node:worker_threads'
import { WorkerAbstract } from './WorkerAbstract.js'
-import { DEFAULT_ELEMENTS_PER_WORKER, EMPTY_FUNCTION, workerSetVersion } from './WorkerConstants.js'
+import {
+ DEFAULT_ELEMENTS_PER_WORKER,
+ EMPTY_FUNCTION,
+ WORKER_SET_VERSION,
+} from './WorkerConstants.js'
import {
type SetInfo,
type UUIDv4,
size: this.size,
started: this.started,
type: 'set',
- version: workerSetVersion,
+ version: WORKER_SET_VERSION,
worker: 'thread',
}
}
export const TEST_CHARGING_STATION_HASH_ID = 'cs-test-hash-001'
/**
- * Timer Intervals (seconds)
+ * Timer Intervals
* Test values for timing-related configuration and expectations
*/
export const TEST_HEARTBEAT_INTERVAL_SECONDS = 60
import { createMockChargingStation } from '../../ChargingStationTestUtils.js'
import { upsertConfigurationKey } from './OCPP20TestUtils.js'
-const DEFAULT_WAIT_MINIMUM_S = 30
-const DEFAULT_RANDOM_RANGE_S = 10
+const DEFAULT_WAIT_MINIMUM_SECONDS = 30
+const DEFAULT_RANDOM_RANGE_SECONDS = 10
const DEFAULT_REPEAT_TIMES = 5
const MS_PER_SECOND = 1000
const delay = OCPP20ServiceUtils.computeReconnectDelay(station, retryCount)
// Assert — retryCount=1 → effectiveRetry=0 → baseDelay = 30s * 2^0 = 30000ms
- const expectedBaseDelayMs = DEFAULT_WAIT_MINIMUM_S * MS_PER_SECOND
- const maxJitterMs = DEFAULT_RANDOM_RANGE_S * MS_PER_SECOND
+ const expectedBaseDelayMs = DEFAULT_WAIT_MINIMUM_SECONDS * MS_PER_SECOND
+ const maxJitterMs = DEFAULT_RANDOM_RANGE_SECONDS * MS_PER_SECOND
assert.ok(delay >= expectedBaseDelayMs, 'delay should be >= default base')
assert.ok(delay < expectedBaseDelayMs + maxJitterMs, 'delay should be < default base + jitter')
})
// Assert — both capped: effectiveRetry = min(retryCount-1, repeatTimes) = 5
// baseDelay = 30s * 2^5 = 960000ms
- const cappedBaseDelayMs = DEFAULT_WAIT_MINIMUM_S * MS_PER_SECOND * 2 ** DEFAULT_REPEAT_TIMES
- const maxJitterMs = DEFAULT_RANDOM_RANGE_S * MS_PER_SECOND
+ const cappedBaseDelayMs =
+ DEFAULT_WAIT_MINIMUM_SECONDS * MS_PER_SECOND * 2 ** DEFAULT_REPEAT_TIMES
+ const maxJitterMs = DEFAULT_RANDOM_RANGE_SECONDS * MS_PER_SECOND
assert.ok(delayBeyondCap >= cappedBaseDelayMs, 'beyond-cap delay should be >= capped base')
assert.ok(
delayBeyondCap < cappedBaseDelayMs + maxJitterMs,
import type { UIServerConfiguration, UUIDv4 } from '../../../src/types/index.js'
import { UIHttpServer } from '../../../src/charging-station/ui-server/UIHttpServer.js'
-import { DEFAULT_COMPRESSION_THRESHOLD } from '../../../src/charging-station/ui-server/UIServerSecurity.js'
+import { DEFAULT_COMPRESSION_THRESHOLD_BYTES } from '../../../src/charging-station/ui-server/UIServerSecurity.js'
import { ApplicationProtocol, ResponseStatus } from '../../../src/types/index.js'
import { standardCleanup } from '../../helpers/TestLifecycleHelpers.js'
import { GZIP_STREAM_FLUSH_DELAY_MS, TEST_UUID } from './UIServerTestConstants.js'
createMockUIServerConfiguration({ type: ApplicationProtocol.HTTP })
const createLargePayload = (status: ResponseStatus = ResponseStatus.SUCCESS) => ({
- data: 'x'.repeat(DEFAULT_COMPRESSION_THRESHOLD + 100),
+ data: 'x'.repeat(DEFAULT_COMPRESSION_THRESHOLD_BYTES + 100),
status,
})
} from '../../../src/charging-station/ui-server/mcp/index.js'
import { AbstractUIService } from '../../../src/charging-station/ui-server/ui-services/AbstractUIService.js'
import { UIMCPServer } from '../../../src/charging-station/ui-server/UIMCPServer.js'
-import { DEFAULT_MAX_PAYLOAD_SIZE } from '../../../src/charging-station/ui-server/UIServerSecurity.js'
+import { DEFAULT_MAX_PAYLOAD_SIZE_BYTES } from '../../../src/charging-station/ui-server/UIServerSecurity.js'
import { BaseError } from '../../../src/exception/index.js'
import {
ApplicationProtocol,
})
await it('should reject with BaseError when payload too large', async () => {
- const oversizedChunk = Buffer.alloc(DEFAULT_MAX_PAYLOAD_SIZE + 1)
+ const oversizedChunk = Buffer.alloc(DEFAULT_MAX_PAYLOAD_SIZE_BYTES + 1)
const mockReq = Readable.from([oversizedChunk])
await assert.rejects(
# Server defaults
DEFAULT_HOST = "127.0.0.1"
DEFAULT_PORT = 9000
-DEFAULT_HEARTBEAT_INTERVAL = 60
+DEFAULT_HEARTBEAT_INTERVAL_SECONDS = 60
+DEFAULT_MESSAGE_TIMEOUT_SECONDS = 30
DEFAULT_TOTAL_COST = 10.0
+DEFAULT_SECURITY_PROFILE = 0
+DEFAULT_CONFIG_SLOT = 1
+DEFAULT_EVSE_ID = 1
+DEFAULT_CONNECTOR_ID = 1
+DEFAULT_OCPP_CSMS_URL = "ws://127.0.0.1:9000"
+DEFAULT_TEST_TOKEN = "test_token" # noqa: S105
+DEFAULT_TOKEN_TYPE = "ISO14443" # noqa: S105
+DEFAULT_VENDOR_ID = "TestVendor"
+DEFAULT_FIRMWARE_URL = "https://example.com/firmware/v2.0.bin"
+DEFAULT_LOG_URL = "https://example.com/logs"
+DEFAULT_CUSTOMER_ID = "test_customer_001"
FALLBACK_TRANSACTION_ID = "test_transaction_123"
MAX_REQUEST_ID = 2**31 - 1
-SHUTDOWN_TIMEOUT = 30.0
+SHUTDOWN_TIMEOUT_SECONDS = 30.0
SUBPROTOCOLS: list[websockets.Subprotocol] = [
websockets.Subprotocol("ocpp2.0"),
websockets.Subprotocol("ocpp2.0.1"),
self._boot_index[0] = idx + 1
return ocpp.v201.call_result.BootNotification(
current_time=datetime.now(timezone.utc).isoformat(),
- interval=DEFAULT_HEARTBEAT_INTERVAL,
+ interval=DEFAULT_HEARTBEAT_INTERVAL_SECONDS,
status=status,
)
async def _send_request_start_transaction(self):
request = ocpp.v201.call.RequestStartTransaction(
- id_token={"id_token": "test_token", "type": "ISO14443"},
- evse_id=1,
+ id_token={"id_token": DEFAULT_TEST_TOKEN, "type": DEFAULT_TOKEN_TYPE},
+ evse_id=DEFAULT_EVSE_ID,
remote_start_id=_random_request_id(),
)
await self.call(request, suppress=False)
await self._call_and_log(request, Action.reset, ResetStatusEnumType.accepted)
async def _send_unlock_connector(self):
- request = ocpp.v201.call.UnlockConnector(evse_id=1, connector_id=1)
+ request = ocpp.v201.call.UnlockConnector(
+ evse_id=DEFAULT_EVSE_ID, connector_id=DEFAULT_CONNECTOR_ID
+ )
await self._call_and_log(
request, Action.unlock_connector, UnlockStatusEnumType.unlocked
)
async def _send_data_transfer(self):
request = ocpp.v201.call.DataTransfer(
- vendor_id="TestVendor", message_id="TestMessage", data="test_data"
+ vendor_id=DEFAULT_VENDOR_ID, message_id="TestMessage", data="test_data"
)
await self._call_and_log(
request, Action.data_transfer, DataTransferStatusEnumType.accepted
request_id=_random_request_id(),
report=True,
clear=False,
- customer_identifier="test_customer_001",
+ customer_identifier=DEFAULT_CUSTOMER_ID,
)
await self._call_and_log(
request,
async def _send_get_log(self):
request = ocpp.v201.call.GetLog(
- log={"remote_location": "https://example.com/logs"},
+ log={"remote_location": DEFAULT_LOG_URL},
log_type=LogEnumType.diagnostics_log,
request_id=_random_request_id(),
)
async def _send_set_network_profile(self):
request = ocpp.v201.call.SetNetworkProfile(
- configuration_slot=1,
+ configuration_slot=DEFAULT_CONFIG_SLOT,
connection_data={
"ocpp_version": "OCPP20",
"ocpp_transport": "JSON",
- "ocpp_csms_url": "ws://127.0.0.1:9000",
- "message_timeout": 30,
- "security_profile": 0,
+ "ocpp_csms_url": DEFAULT_OCPP_CSMS_URL,
+ "message_timeout": DEFAULT_MESSAGE_TIMEOUT_SECONDS,
+ "security_profile": DEFAULT_SECURITY_PROFILE,
"ocpp_interface": "Wired0",
},
)
request = ocpp.v201.call.UpdateFirmware(
request_id=_random_request_id(),
firmware={
- "location": "https://example.com/firmware/v2.0.bin",
+ "location": DEFAULT_FIRMWARE_URL,
"retrieve_date_time": datetime.now(timezone.utc).isoformat(),
},
)
await shutdown_event.wait()
try:
- async with asyncio.timeout(SHUTDOWN_TIMEOUT):
+ async with asyncio.timeout(SHUTDOWN_TIMEOUT_SECONDS):
await server.wait_closed()
except TimeoutError:
logger.warning(
"Shutdown timed out after %.0fs"
" — connections may not have closed cleanly",
- SHUTDOWN_TIMEOUT,
+ SHUTDOWN_TIMEOUT_SECONDS,
)
logger.info("Server shutdown complete")
)
from server import (
- DEFAULT_HEARTBEAT_INTERVAL,
+ DEFAULT_HEARTBEAT_INTERVAL_SECONDS,
DEFAULT_TOTAL_COST,
FALLBACK_TRANSACTION_ID,
MAX_REQUEST_ID,
reason="PowerUp",
)
assert response.status == RegistrationStatusEnumType.accepted
- assert response.interval == DEFAULT_HEARTBEAT_INTERVAL
+ assert response.interval == DEFAULT_HEARTBEAT_INTERVAL_SECONDS
assert isinstance(response.current_time, str)
assert "T" in response.current_time
reason="PowerUp",
)
assert response.status == RegistrationStatusEnumType.accepted
- assert response.interval == DEFAULT_HEARTBEAT_INTERVAL
+ assert response.interval == DEFAULT_HEARTBEAT_INTERVAL_SECONDS
class TestHeartbeatHandler:
<template>
<router-view />
<Container
- v-show="$route.name !== 'charging-stations' && $route.name !== 'not-found'"
+ v-show="$route.name !== ROUTE_NAMES.CHARGING_STATIONS && $route.name !== ROUTE_NAMES.NOT_FOUND"
id="action-container"
class="action-container"
>
<script setup lang="ts">
import Container from '@/components/Container.vue'
+import { ROUTE_NAMES } from '@/composables'
</script>
<style scoped>
})
.finally(() => {
resetToggleButtonState('add-charging-stations', true)
- $router.push({ name: 'charging-stations' })
+ $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS })
})
.catch((error: Error) => {
$toast.error('Error at adding charging stations')
convertToBoolean,
randomUUID,
resetToggleButtonState,
+ ROUTE_NAMES,
useTemplates,
useUIClient,
} from '@/composables'
})
.finally(() => {
resetToggleButtonState(`${props.hashId}-set-supervision-url`, true)
- $router.push({ name: 'charging-stations' })
+ $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS })
})
.catch((error: Error) => {
$toast.error('Error at setting supervision url')
import { ref } from 'vue'
import Button from '@/components/buttons/Button.vue'
-import { resetToggleButtonState, useUIClient } from '@/composables'
+import { resetToggleButtonState, ROUTE_NAMES, useUIClient } from '@/composables'
const props = defineProps<{
chargingStationId: string
import { useToast } from 'vue-toast-notification'
import Button from '@/components/buttons/Button.vue'
-import { convertToInt, resetToggleButtonState, UIClient, useUIClient } from '@/composables'
+import {
+ convertToInt,
+ resetToggleButtonState,
+ ROUTE_NAMES,
+ UIClient,
+ useUIClient,
+} from '@/composables'
import { type OCPPVersion } from '@/types'
const props = defineProps<{
$toast.error('Error at authorizing RFID tag')
console.error('Error at authorizing RFID tag:', error)
resetToggleButtonState(toggleButtonId.value, true)
- $router.push({ name: 'charging-stations' })
+ $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS })
return
}
}
console.error('Error at starting transaction:', error)
} finally {
resetToggleButtonState(toggleButtonId.value, true)
- $router.push({ name: 'charging-stations' })
+ $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS })
}
}
</script>
{{ evseId != null ? `${evseId}/${connectorId}` : connectorId }}
</td>
<td class="connectors-table__column">
- {{ connector.status ?? 'Ø' }}
+ {{ connector.status ?? EMPTY_VALUE_PLACEHOLDER }}
</td>
<td class="connectors-table__column">
{{ connector.locked === true ? 'Yes' : 'No' }}
:id="`${hashId}-${evseId ?? 0}-${connectorId}-start-transaction`"
:off="
() => {
- $router.push({ name: 'charging-stations' })
+ $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS })
}
"
:on="
() => {
$router.push({
- name: 'start-transaction',
+ name: ROUTE_NAMES.START_TRANSACTION,
params: { hashId, chargingStationId, connectorId },
query: {
...(evseId != null ? { evseId: String(evseId) } : {}),
import Button from '@/components/buttons/Button.vue'
import StateButton from '@/components/buttons/StateButton.vue'
import ToggleButton from '@/components/buttons/ToggleButton.vue'
-import { useExecuteAction, useUIClient } from '@/composables'
+import { EMPTY_VALUE_PLACEHOLDER, ROUTE_NAMES, useExecuteAction, useUIClient } from '@/composables'
const props = defineProps<{
atgStatus?: Status
{{ getWebSocketStateName(chargingStation.wsState) }}
</td>
<td class="cs-table__column">
- {{ chargingStation.bootNotificationResponse?.status ?? 'Ø' }}
+ {{ chargingStation.bootNotificationResponse?.status ?? EMPTY_VALUE_PLACEHOLDER }}
</td>
<td class="cs-table__column">
- {{ chargingStation.stationInfo.ocppVersion ?? 'Ø' }}
+ {{ chargingStation.stationInfo.ocppVersion ?? EMPTY_VALUE_PLACEHOLDER }}
</td>
<td class="cs-table__column">
{{ chargingStation.stationInfo.templateName }}
{{ chargingStation.stationInfo.chargePointModel }}
</td>
<td class="cs-table__column">
- {{ chargingStation.stationInfo.firmwareVersion ?? 'Ø' }}
+ {{ chargingStation.stationInfo.firmwareVersion ?? EMPTY_VALUE_PLACEHOLDER }}
</td>
<td class="cs-table__column">
<StateButton
:id="`${chargingStation.stationInfo.hashId}-set-supervision-url`"
:off="
() => {
- $router.push({ name: 'charging-stations' })
+ $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS })
}
"
:on="
() => {
$router.push({
- name: 'set-supervision-url',
+ name: ROUTE_NAMES.SET_SUPERVISION_URL,
params: {
hashId: chargingStation.stationInfo.hashId,
chargingStationId: chargingStation.stationInfo.chargingStationId,
import CSConnector from '@/components/charging-stations/CSConnector.vue'
import {
deleteLocalStorageByKeyPattern,
+ EMPTY_VALUE_PLACEHOLDER,
getWebSocketStateName,
+ ROUTE_NAMES,
useExecuteAction,
useUIClient,
} from '@/composables'
// Local UI project constants
+export const EMPTY_VALUE_PLACEHOLDER = 'Ø'
+
+export const ROUTE_NAMES = {
+ ADD_CHARGING_STATIONS: 'add-charging-stations',
+ CHARGING_STATIONS: 'charging-stations',
+ NOT_FOUND: 'not-found',
+ SET_SUPERVISION_URL: 'set-supervision-url',
+ START_TRANSACTION: 'start-transaction',
+} as const
+
export const SHARED_TOGGLE_BUTTON_KEY_PREFIX = 'shared-toggle-button-'
export const TOGGLE_BUTTON_KEY_PREFIX = 'toggle-button-'
export const UI_SERVER_CONFIGURATION_INDEX_KEY = 'uiServerConfigurationIndex'
export {
+ EMPTY_VALUE_PLACEHOLDER,
+ ROUTE_NAMES,
SHARED_TOGGLE_BUTTON_KEY_PREFIX,
TOGGLE_BUTTON_KEY_PREFIX,
UI_SERVER_CONFIGURATION_INDEX_KEY,
getFromLocalStorage,
setToLocalStorage,
templatesKey,
+ UI_SERVER_CONFIGURATION_INDEX_KEY,
UIClient,
uiClientKey,
} from '@/composables'
const templates = ref<string[]>([])
const chargingStations = ref<ChargingStationData[]>([])
if (
- getFromLocalStorage<number | undefined>('uiServerConfigurationIndex', undefined) == null ||
- getFromLocalStorage('uiServerConfigurationIndex', 0) >
+ getFromLocalStorage<number | undefined>(UI_SERVER_CONFIGURATION_INDEX_KEY, undefined) == null ||
+ getFromLocalStorage(UI_SERVER_CONFIGURATION_INDEX_KEY, 0) >
(configuration.value.uiServer as UIServerConfigurationSection[]).length - 1
) {
- setToLocalStorage('uiServerConfigurationIndex', 0)
+ setToLocalStorage(UI_SERVER_CONFIGURATION_INDEX_KEY, 0)
}
const uiClient = UIClient.getInstance(
(configuration.value.uiServer as UIServerConfigurationSection[])[
- getFromLocalStorage('uiServerConfigurationIndex', 0)
+ getFromLocalStorage(UI_SERVER_CONFIGURATION_INDEX_KEY, 0)
]
)
app.provide(configurationKey, configuration)
import AddChargingStations from '@/components/actions/AddChargingStations.vue'
import SetSupervisionUrl from '@/components/actions/SetSupervisionUrl.vue'
import StartTransaction from '@/components/actions/StartTransaction.vue'
+import { ROUTE_NAMES } from '@/composables'
import ChargingStationsView from '@/views/ChargingStationsView.vue'
import NotFoundView from '@/views/NotFoundView.vue'
components: {
default: ChargingStationsView,
},
- name: 'charging-stations',
+ name: ROUTE_NAMES.CHARGING_STATIONS,
path: '/',
},
{
action: AddChargingStations,
default: ChargingStationsView,
},
- name: 'add-charging-stations',
+ name: ROUTE_NAMES.ADD_CHARGING_STATIONS,
path: '/add-charging-stations',
},
{
action: SetSupervisionUrl,
default: ChargingStationsView,
},
- name: 'set-supervision-url',
+ name: ROUTE_NAMES.SET_SUPERVISION_URL,
path: '/set-supervision-url/:hashId/:chargingStationId',
props: { action: true, default: false },
},
action: StartTransaction,
default: ChargingStationsView,
},
- name: 'start-transaction',
+ name: ROUTE_NAMES.START_TRANSACTION,
path: '/start-transaction/:hashId/:chargingStationId/:connectorId',
props: { action: true, default: false },
},
components: {
default: NotFoundView,
},
- name: 'not-found',
+ name: ROUTE_NAMES.NOT_FOUND,
path: '/:pathMatch(.*)*',
},
],
)
clearToggleButtons()
refresh()
- $route.name !== 'charging-stations' &&
- $router.push({ name: 'charging-stations' })
+ $route.name !== ROUTE_NAMES.CHARGING_STATIONS &&
+ $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS })
},
{ once: true }
)
:key="state.renderAddChargingStations"
:off="
() => {
- $router.push({ name: 'charging-stations' })
+ $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS })
}
"
:on="
() => {
- $router.push({ name: 'add-charging-stations' })
+ $router.push({ name: ROUTE_NAMES.ADD_CHARGING_STATIONS })
}
"
:shared="true"
deleteLocalStorageByKeyPattern,
getFromLocalStorage,
randomUUID,
+ ROUTE_NAMES,
setToLocalStorage,
+ TOGGLE_BUTTON_KEY_PREFIX,
UI_SERVER_CONFIGURATION_INDEX_KEY,
useChargingStations,
useConfiguration,
}
const clearToggleButtons = (): void => {
- deleteLocalStorageByKeyPattern('toggle-button')
+ deleteLocalStorageByKeyPattern(TOGGLE_BUTTON_KEY_PREFIX)
}
const $configuration = useConfiguration()