Rychlý start — Voke ESM (CZ)
Kompletní průchod integrací proti živé Voke organizaci. Odhadovaný čas: 15 minut.
Tato příručka vás provede od nuly až po živou telemetrii proti Voke organizaci. Na konci budete mít potvrzené AMQP fronty, publikovaný příkaz a běžící konzumenta telemetrie — vše s použitím přihlašovacích údajů vygenerovaných průvodcem Connect partner na /orgs/<orgId>/settings/connections.
Chcete celý projekt ke klonování? lefteq/voke-partner-examples
obsahuje ověřené, spustitelné Java (Spring Boot) a TypeScript klienty — snippety níže shrnuté do
git clone-a-spusť repozitáře.
Předpoklady
- Běžící instance Voke (lokálně:
bun run docker:up && bun run dev:api, nebo vzdálené nasazení). - Org admin cílové Voke organizace, který vám může vygenerovat partnerské klíče.
- Node 18 nebo novější pro spuštění TypeScript příkladů.
Krok 1: Získejte partnerské přihlašovací údaje od svého org admina
Produkční partneři si AMQP nepovolují sami. Požádejte svého org admina, aby pro vaši integraci vygeneroval klíč pomocí průvodce Connect partner:
- Přihlaste se na
https://voke.turena.cza vyberte správnou organizaci v přepínači organizací. - Otevřete Organization settings → Connections (
/orgs/<orgId>/settings/connections?tab=api-keys). - Klikněte na Connect partner a projděte třístupňového průvodce. Každý krok má vlastní URL, takže se na něj můžete přímo odkázat nebo stránku obnovit, aniž byste ztratili postup; stav je uložen v
sessionStoragea automaticky vymazán při úspěšném Generate nebo Cancel:- Use case —
/orgs/<orgId>/settings/connections/connect-partner— vyberte přednastavení, které odpovídá vaší integraci (Trading platform partner, Telemetry consumer (read-only), HTTP read-only, Internal tool nebo Custom). - Permissions —
.../connect-partner/permissions— zkontrolujte předvybranou saduApiKeyScope; v případě potřeby ji zužte. - Network —
.../connect-partner/network— volitelně omezte klíč na CIDR allowlist, vyplňte čitelný název + partner ID a klikněte na Generate. Voke spustí serverový broker self-test, než přesměruje na detail API klíče, kde se zobrazí banner s jednorázovým odhalením.
- Use case —
- Předejte partnerovi balíček (bundle) z odhalovacího banneru: AMQPS URI, raw API key a (pokud je přítomen scope
vcp:write:*) HMAC signing key. Klikněte na I've saved these pro zavření; obnovení detailu po tomto kroku tajné hodnoty znovu nezobrazí. Ukázky kódu na záložce Quickstart téže stránky jsou předvyplněné org slugem — zkopírujte je jako výchozí bod.
Balíček se zobrazí přesně jednou. Uložte AMQPS URI, raw key a signing key do správce tajných hodnot (secret manager) předtím, než na odhalovací obrazovce kliknete na I've saved these. Obnova neexistuje — pokud se jakákoli hodnota ztratí, klíč rotujte (vygenerujte nový, starý zneplatněte). Vzor pro rotaci viz API keys & auth.
Záložka Quickstart na /orgs/<orgId>/settings/connections?tab=quickstart je zdrojem pravdy
pro Node, Python a curl úryvky připravené k vložení — sestavuje je server se skutečným org slugem
a placeholderem <your-key>. Níže uvedené ukázky zrcadlí tento výstup s dalším inline komentářem.
Kompletní mapu admin tras viz Organization settings routes.
Krok 2: Nastavte si integrátorský projekt
Vytvořte nový Node projekt a nainstalujte klientskou knihovnu AMQP:
mkdir voke-esm-quickstart && cd voke-esm-quickstart
bun init
bun add amqplib
bun add -d @types/amqplib tsx typescriptKrok 3: Přidejte pomocník pro připojení
Vytvořte amqp-connect.ts. Nejjednodušší cesta je předat AMQPS URI přímo z odhalovací obrazovky — již obsahuje org slug jako AMQP uživatelské jméno, raw key jako heslo a vhost specifický pro klíč (partner-{keyId}) jako cestu:
// amqp-connect.ts
// Connect to Voke's RabbitMQ using the AMQPS URI from the Connect partner wizard.
// The URI shape is:
// amqps://{org-slug}:{url-encoded-key}@amqp.voke.turena.cz:5671/partner-{keyId}
import * as amqp from 'amqplib';
export interface VokeAmqpCreds {
/**
* The full AMQPS URI from the Connections reveal screen.
* Contains org slug (username), raw API key (password), host, port, and
* per-key vhost (`partner-{keyId}`) — no manual assembly required.
*/
amqpsUri: string;
/** Your organization slug — used for queue/routing-key prefixes only. */
orgSlug: string;
}
export async function connectVoke(creds: VokeAmqpCreds) {
// The TLS-1.3-only broker requires SNI; amqplib will not infer the servername
// from the URI, so pass it explicitly or the TLS handshake fails.
const conn = await amqp.connect(creds.amqpsUri, {
servername: new URL(creds.amqpsUri).hostname,
});
const ch = await conn.createChannel();
const prefix = `vcp.${creds.orgSlug}`;
// Queues are asserted (and bound to the `vcp` exchange) by Voke when
// trading is enabled for your org. checkQueue verifies they exist before
// any publish or consume.
const inbound = {
commands: `${prefix}.command`,
config: `${prefix}.config`,
schedule: `${prefix}.schedule`,
};
const outbound = {
telemetry: `${prefix}.event.telemetry`,
status: `${prefix}.event.status`,
alarm: `${prefix}.event.alarm`,
execution: `${prefix}.event.execution`,
};
for (const q of [...Object.values(inbound), ...Object.values(outbound)]) {
await ch.checkQueue(q);
}
return { conn, ch, exchange: 'vcp', prefix, inbound, outbound };
}Krok 4: Ověřte připojení
Vytvořte run-connect.ts a spuštěním ověřte, že AMQP autentizace funguje a všechny fronty existují:
// run-connect.ts
import { connectVoke } from './amqp-connect';
(async () => {
const { conn, inbound, outbound } = await connectVoke({
// Paste the AMQPS URI from the Connections reveal screen.
amqpsUri: 'amqps://YOUR-ORG-SLUG:URL-ENCODED-KEY@amqp.voke.turena.cz:5671/partner-XXXX',
orgSlug: 'YOUR-ORG-SLUG',
});
console.log('inbound queues OK:', inbound);
console.log('outbound queues OK:', outbound);
await conn.close();
})();npx tsx run-connect.ts
# Expected:
# inbound queues OK: { commands: 'vcp.acme.command', config: 'vcp.acme.config', schedule: 'vcp.acme.schedule' }
# outbound queues OK: { telemetry: 'vcp.acme.event.telemetry', status: 'vcp.acme.event.status', ... }Pokud checkQueue vyhodí chybu, fronty pro vaši org nejsou nasazené (asserted). Broker self-test v průvodci by to měl zachytit ještě před odhalením balíčku — pokud na to narazíte v praxi, požádejte svého org admina, aby pomocí tlačítka Test connection na detailu API klíče (/orgs/<orgId>/settings/connections/api-keys/<apiKeyId>) znovu spustil sondu a vypsal chybu brokeru.
Krok 5: Přidejte publisher příkazů
Vytvořte publish-command.ts. Příkazy se publikují do topic exchange vcp s routing key {slug}.command.{commandType} — konzument Voke je vyzvedne z vcp.{slug}.command. Site setpointy jsou autentizovány dvojicí AMQP přihlašovacích údajů a scopem vcp:write:setpoint. Rizikovější trasy (command.device, command.mode, schedule.*) vyžadují navíc HMAC podpis; viz VCP message integrity.
// publish-command.ts
// Publish a VCP command by routing it through the `vcp` exchange.
import { randomUUID } from 'node:crypto';
import { connectVoke, type VokeAmqpCreds } from './amqp-connect';
export interface VcpCommandInput {
commandType: string; // e.g. 'site-setpoint', 'mode'
siteId: string; // target Voke site / plant identifier
source: string; // your partner ID (e.g. 'my-esm')
payload: Record<string, unknown>; // shape depends on commandType
correlationId?: string; // echoed back in event.status for correlation
}
export async function publishCommand(creds: VokeAmqpCreds, input: VcpCommandInput) {
const { ch, exchange, conn } = await connectVoke(creds);
const envelope = {
version: '1.1' as const,
messageId: randomUUID(),
correlationId: input.correlationId,
timestamp: new Date().toISOString(),
source: input.source,
siteId: input.siteId,
payload: input.payload,
};
const routingKey = `${creds.orgSlug}.command.${input.commandType}`;
ch.publish(exchange, routingKey, Buffer.from(JSON.stringify(envelope)), {
contentType: 'application/json',
persistent: true,
messageId: envelope.messageId,
correlationId: envelope.correlationId,
timestamp: Date.parse(envelope.timestamp),
});
await ch.close();
await conn.close();
}Poté vytvořte run-publish.ts pro odeslání příkazu site-setpoint na vaši fake plant:
// run-publish.ts
import { publishCommand } from './publish-command';
(async () => {
await publishCommand(
{
amqpsUri: 'amqps://YOUR-ORG-SLUG:URL-ENCODED-KEY@amqp.voke.turena.cz:5671/partner-XXXX',
orgSlug: 'YOUR-ORG-SLUG',
},
{
commandType: 'site-setpoint',
siteId: 'YOUR-FAKE-PLANT-EXTERNAL-ID',
source: 'YOUR-PARTNER-ID',
payload: {
type: 'POWER',
targetValueKw: 10,
direction: 'EXPORT',
includeConsumption: true,
priority: 'NORMAL',
validFrom: new Date().toISOString(),
},
},
);
console.log('command published');
})();npx tsx run-publish.ts
# Expected: command publishedZkontrolujte Voke admin pod Plants → [vaše plant] → Commands a uvidíte příkaz zalogovaný se stavem EXECUTED nebo REJECTED (rejected znamená, že fake plant zatím není napojena na skutečnou Voke plant — pro tento krok je to v pořádku).
Krok 6: Přidejte konzumenta telemetrie
Vytvořte consume-telemetry.ts. Voke publikuje telemetrické události do vcp.{slug}.event.telemetry s routing keys {slug}.event.telemetry.realtime.{siteId} a {slug}.event.telemetry.meter.{siteId}. Výchozí fronta je svázána (bound) s #, takže přijímá každou site; spárujte segment typu routing key (.realtime. / .meter.) namísto přesného suffixu. Každá zpráva je VCP envelope obsahující telemetrický payload.
// consume-telemetry.ts
// Subscribe to telemetry events emitted by Voke on your org's outbound
// event.telemetry queue.
import { connectVoke, type VokeAmqpCreds } from './amqp-connect';
export interface VcpEnvelope<T = unknown> {
version: '1.1';
messageId: string;
correlationId?: string;
timestamp: string;
source: string;
siteId: string;
payload: T;
}
export async function consumeTelemetry(
creds: VokeAmqpCreds,
onMessage: (envelope: VcpEnvelope) => void,
) {
const { ch, outbound } = await connectVoke(creds);
await ch.consume(outbound.telemetry, (msg) => {
if (!msg) return;
try {
const envelope = JSON.parse(msg.content.toString()) as VcpEnvelope;
onMessage(envelope);
ch.ack(msg);
} catch {
// Malformed payload — dead-letter rather than redeliver.
ch.nack(msg, false, false);
}
});
}Poté vytvořte runner, který loguje telemetrii po dobu 30 sekund:
// run-consume.ts
import { consumeTelemetry } from './consume-telemetry';
(async () => {
console.log('Listening for telemetry for 30 seconds...');
await consumeTelemetry(
{
amqpsUri: 'amqps://YOUR-ORG-SLUG:URL-ENCODED-KEY@amqp.voke.turena.cz:5671/partner-XXXX',
orgSlug: 'YOUR-ORG-SLUG',
},
(envelope) => {
console.log('telemetry siteId:', envelope.siteId);
console.log('telemetry payload:', JSON.stringify(envelope.payload, null, 2));
},
);
await new Promise((resolve) => setTimeout(resolve, 30_000));
process.exit(0);
})();npx tsx run-consume.tsTelemetrické zprávy budete přijímat až poté, co Voke plant bude mít externalPlantId nastavené
tak, aby odpovídalo externalPlantId fake plant, a tato plant bude publikovat živá data přes
MQTT. Pro smoke test bez partnera viz PLC quick start pro simulaci plant
odesílající telemetrii.
Další kroky
- Per-org AMQP queues — kompletní sémantika front, routing keys, durability a konfigurace dead-letter.
- VCP data model — kanonické tvary payloadů pro příkazy a telemetrické envelopy.
- Errors & retries — error envelope, kódy zamítnutí (rejection codes), idempotence a chování při opětovném připojení.
Quick start — Voke ESM
End-to-end integration walkthrough against a live Voke org. Estimated time: 15 minutes.
Spring Boot example
A complete, copy-pasteable Spring Boot (Java) integration that consumes realtime telemetry over AMQPS and publishes commands back to Voke, including HMAC signing for high-stakes commands.