Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PiShock Service #1166

Merged
merged 3 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ nodecg-io is the successor of [ChatOverflow](https://github.com/codeoverflow-org
- OBS
- [OpenTTS](https://github.com/synesthesiam/opentts)
- Philips Hue
- [PiShock](https://pishock.com)
- RCON
- Reddit
- sACN Receiver
Expand Down
56 changes: 49 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions samples/pishock/extension/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import NodeCG from "@nodecg/types";
import { PiShockClient } from "nodecg-io-pishock";
import { requireService } from "nodecg-io-core";

module.exports = function (nodecg: NodeCG.ServerAPI) {
nodecg.log.info("Sample bundle for the PiShock service started.");

const pishock = requireService<PiShockClient>(nodecg, "pishock");

pishock?.onAvailable((client) => {
nodecg.log.info("PiShock client has been updated, printing all device's infos");
client.connectedDevices.forEach(async (device) => {
const info = await device.getInfo();
nodecg.log.info(
`Client ID: ${info.clientId}, ID: ${info.id}, Name: ${info.name}, Paused: ${info.paused}, ` +
`MaxIntensity: ${info.maxIntensity}, MaxDuration: ${info.maxDuration}, Online: ${info.online}`,
);
});
});

pishock?.onUnavailable(() => {
nodecg.log.info("PiShock service unavailable.");
});
};
20 changes: 20 additions & 0 deletions samples/pishock/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "pishock",
"version": "0.3.0",
"private": true,
"nodecg": {
"compatibleRange": ">=1.1.1 <3.0.0",
"bundleDependencies": {
"nodecg-io-pishock": "^0.3.0"
}
},
"license": "MIT",
"dependencies": {
"@types/node": "^20.12.2",
"@nodecg/types": "^2.1.12",
"nodecg-io-core": "^0.3.0",
"nodecg-io-pishock": "^0.3.0",
"typescript": "^5.4.3",
"nodecg-io-tsconfig": "^1.0.0"
}
}
11 changes: 11 additions & 0 deletions samples/pishock/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "nodecg-io-tsconfig",
"references": [
{
"path": "../../nodecg-io-core"
},
{
"path": "../../services/nodecg-io-template"
}
]
}
2 changes: 1 addition & 1 deletion services/nodecg-io-gametts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"contributors": [
{
"name": "SteffoSpieler",
"url": "https://about.steffospieler.de"
"url": "https://steffo.dev"
}
],
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion services/nodecg-io-google-cast/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"contributors": [
{
"name": "SteffoSpieler",
"url": "https://about.steffospieler.de"
"url": "https://steffo.dev"
}
],
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion services/nodecg-io-opentts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"contributors": [
{
"name": "SteffoSpieler",
"url": "https://about.steffospieler.de"
"url": "https://steffo.dev"
}
],
"repository": {
Expand Down
65 changes: 65 additions & 0 deletions services/nodecg-io-pishock/extension/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import NodeCG from "@nodecg/types";
import { Result, emptySuccess, success, ServiceBundle, Logger, error } from "nodecg-io-core";
import { PiShockDevice, PiShockAuthentication } from "pishock-ts";

export interface PiShockConfig {
authentications: Array<PiShockAuthentication>;
}

export interface PiShockClient {
connectedDevices: Array<PiShockDevice>;
}

module.exports = (nodecg: NodeCG.ServerAPI) => {
new PiShockService(nodecg, "pishock", __dirname, "../schema.json").register();
};

class PiShockService extends ServiceBundle<PiShockConfig, PiShockClient> {
async validateConfig(config: PiShockConfig): Promise<Result<void>> {
for (const deviceConfig of config.authentications) {
if (!/[0-9a-f-]+/.test(deviceConfig.apiKey)) {
return error(`Invalid PiShock apiKey format: ${deviceConfig.apiKey}`);
}

if (!/[0-9A-Z]+/.test(deviceConfig.code)) {
return error(`Invalid PiShock code format: ${deviceConfig.code}`);
}
}

return emptySuccess();
}

async createClient(config: PiShockConfig, logger: Logger): Promise<Result<PiShockClient>> {
const devices = config.authentications.map((c) => {
// Set name if missing.
c.name ??= "nodecg-io PiShock Service";
return new PiShockDevice(c);
});

// Test connection and return error if any provided auth details fail to do the request.
const connectionStates = await Promise.all(
devices.map(async (dev) => {
try {
await dev.getInfo();
return true;
} catch (err) {
return err;
}
}),
);

for (const state of connectionStates) {
if (state instanceof Error) {
return error(`Failed to connect to PiShock api: ${state.message}`);
}
}

const client = { connectedDevices: devices };
logger.info("Successfully created PiShock client.");
return success(client);
}

stopClient(_: PiShockClient, _logger: Logger): void {
// Stateless REST API, cannot be stopped
}
}
50 changes: 50 additions & 0 deletions services/nodecg-io-pishock/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "nodecg-io-pishock",
"version": "0.3.0",
"description": "Allows using the PiShock api.",
"homepage": "https://nodecg.io/RELEASE/samples/pishock",
"author": {
"name": "CodeOverflow team",
"url": "https://github.com/codeoverflow-org"
},
"contributors": [
{
"name": "SteffoSpieler",
"url": "https://steffo.dev"
}
],
"repository": {
"type": "git",
"url": "https://github.com/codeoverflow-org/nodecg-io.git",
"directory": "services/nodecg-io-pishock"
},
"files": [
"**/*.js",
"**/*.js.map",
"**/*.d.ts",
"*.json"
],
"main": "extension/index",
"keywords": [
"nodecg-io",
"nodecg-bundle",
"pishock"
],
"nodecg": {
"compatibleRange": ">=1.1.1 <3.0.0",
"bundleDependencies": {
"nodecg-io-core": "^0.3.0"
}
},
"license": "MIT",
"devDependencies": {
"@types/node": "^20.12.2",
"@nodecg/types": "^2.1.12",
"typescript": "^5.4.3",
"nodecg-io-tsconfig": "^1.0.0"
},
"dependencies": {
"nodecg-io-core": "^0.3.0",
"pishock-ts": "^1.0.1"
}
}
33 changes: 33 additions & 0 deletions services/nodecg-io-pishock/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": false,
"properties": {
"authentications": {
"type": "array",
"items": {
"type": "object",
"required": ["username", "apiKey", "code"],
"properties": {
"username": {
"type": "string",
"description": "Username you use to log into PiShock.com. Can be found in the Account section of the website."
},
"apiKey": {
"type": "string",
"description": "API Key generated on PiShock.com. Can be found in the Account section of the website."
},
"code": {
"type": "string",
"description": "Sharecode generated on PiShock.com. Limitations can be set when generating the code."
},
"name": {
"type": "string",
"description": "Name of what sent the commands. This will show up in the PiShock logs on the website."
}
}
}
}
},
"required": ["authentications"]
}
8 changes: 8 additions & 0 deletions services/nodecg-io-pishock/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "nodecg-io-tsconfig",
"references": [
{
"path": "../../nodecg-io-core"
}
]
}