| 
 | 1 | +import NodeCG from "@nodecg/types";  | 
 | 2 | +import { Result, emptySuccess, success, ServiceBundle, Logger, error } from "nodecg-io-core";  | 
 | 3 | +import { PiShockDevice, PiShockAuthentication } from "pishock-ts";  | 
 | 4 | + | 
 | 5 | +export interface PiShockConfig {  | 
 | 6 | +    authentications: Array<PiShockAuthentication>;  | 
 | 7 | +}  | 
 | 8 | + | 
 | 9 | +export interface PiShockClient {  | 
 | 10 | +    connectedDevices: Array<PiShockDevice>;  | 
 | 11 | +}  | 
 | 12 | + | 
 | 13 | +module.exports = (nodecg: NodeCG.ServerAPI) => {  | 
 | 14 | +    new PiShockService(nodecg, "pishock", __dirname, "../schema.json").register();  | 
 | 15 | +};  | 
 | 16 | + | 
 | 17 | +class PiShockService extends ServiceBundle<PiShockConfig, PiShockClient> {  | 
 | 18 | +    async validateConfig(config: PiShockConfig): Promise<Result<void>> {  | 
 | 19 | +        for (const deviceConfig of config.authentications) {  | 
 | 20 | +            if (!/[0-9a-f-]+/.test(deviceConfig.apiKey)) {  | 
 | 21 | +                return error(`Invalid PiShock apiKey format: ${deviceConfig.apiKey}`);  | 
 | 22 | +            }  | 
 | 23 | + | 
 | 24 | +            if (!/[0-9A-Z]+/.test(deviceConfig.code)) {  | 
 | 25 | +                return error(`Invalid PiShock code format: ${deviceConfig.code}`);  | 
 | 26 | +            }  | 
 | 27 | +        }  | 
 | 28 | + | 
 | 29 | +        return emptySuccess();  | 
 | 30 | +    }  | 
 | 31 | + | 
 | 32 | +    async createClient(config: PiShockConfig, logger: Logger): Promise<Result<PiShockClient>> {  | 
 | 33 | +        const devices = config.authentications.map((c) => {  | 
 | 34 | +            // Set name if missing.  | 
 | 35 | +            c.name ??= "nodecg-io PiShock Service";  | 
 | 36 | +            return new PiShockDevice(c);  | 
 | 37 | +        });  | 
 | 38 | + | 
 | 39 | +        // Test connection and return error if any provided auth details fail to do the request.  | 
 | 40 | +        const connectionStates = await Promise.all(  | 
 | 41 | +            devices.map(async (dev) => {  | 
 | 42 | +                try {  | 
 | 43 | +                    await dev.getInfo();  | 
 | 44 | +                    return true;  | 
 | 45 | +                } catch (err) {  | 
 | 46 | +                    return err;  | 
 | 47 | +                }  | 
 | 48 | +            }),  | 
 | 49 | +        );  | 
 | 50 | + | 
 | 51 | +        for (const state of connectionStates) {  | 
 | 52 | +            if (state instanceof Error) {  | 
 | 53 | +                return error(`Failed to connect to PiShock api: ${state.message}`);  | 
 | 54 | +            }  | 
 | 55 | +        }  | 
 | 56 | + | 
 | 57 | +        const client = { connectedDevices: devices };  | 
 | 58 | +        logger.info("Successfully created PiShock client.");  | 
 | 59 | +        return success(client);  | 
 | 60 | +    }  | 
 | 61 | + | 
 | 62 | +    stopClient(_: PiShockClient, _logger: Logger): void {  | 
 | 63 | +        // Stateless REST API, cannot be stopped  | 
 | 64 | +    }  | 
 | 65 | +}  | 
0 commit comments