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