const { ApolloServer, gql, PubSub } = require("apollo-server"); const Indi = require("@atoy40/indijs"); const fs = require("fs"); const IndiDriverInterface = Indi.IndiDriverInterface; // A schema is a collection of type definitions (hence "typeDefs") // that together define the "shape" of queries that are executed against // your data. const typeDefs = gql` enum DriverInterface { GENERAL TELESCOPE CCD GUIDER FOCUSER FILTER DOME GPS WEATHER AO DUSTCAP LIGHTBOX DETECTOR ROTATOR SPECTROGRAPH CORRELATOR AUX } type Property { id: String! name: String vector: Vector device: String permission: Int } interface Vector { id: String! name: String! label: String group: String device: String state: Int permission: Int } type NumberVector implements Vector { id: String! name: String! label: String group: String device: String state: Int permission: Int values: [NumberValue] } type SwitchVector implements Vector { id: String! name: String! label: String group: String device: String state: Int rule: Int permission: Int values: [SwitchValue] } type TextVector implements Vector { id: String! name: String! label: String group: String device: String state: Int permission: Int values: [TextValue] } type LightVector implements Vector { id: String! name: String! label: String group: String device: String state: Int permission: Int values: [LightValue] } type NumberValue { name: String! label: String formated: String value: Float min: Float max: Float step: Float } type SwitchValue { name: String! label: String value: Boolean } type TextValue { name: String! label: String value: String } type LightValue { name: String! label: String value: Int } type Device { id: String! name: String properties: [Property] drivers: Int connected: Boolean } type Message { id: String device: Device! message: String! } input NumberValueInput { name: String! value: Float! } input NumberInput { device: String! property: String! values: [NumberValueInput!]! } input OneNumberInput { device: String! property: String! name: String! value: Float! } input SwitchValueInput { name: String! value: Boolean! } input SwitchInput { device: String! property: String! values: [SwitchValueInput!]! } input OneTextInput { device: String! property: String! name: String! value: String! } type Query { connected: Boolean devices: [Device] device(name: String!): Device } type Subscription { connected: Boolean disconnected: Int newDevice: Device newProperty: Property newValue: Vector newMessage: Message } type Mutation { connect: Boolean disconnect: Boolean connectDevice(id: String!): Device sendOneNumber(input: OneNumberInput!): Boolean sendNumber(input: NumberInput!): Boolean sendSwitch(input: SwitchInput!): Boolean sendOneText(input: OneTextInput!): Boolean } `; const indi = new Indi.Client("localhost"); const pubsub = new PubSub(); indi .on("connected", () => { pubsub.publish(["CONNECTED"], { connected: true }); }) .on("disconnected", (code) => { pubsub.publish(["CONNECTED"], { connected: false }); pubsub.publish(["DISCONNECTED"], { disconnected: code }); }) .on("newDevice", (device) => { pubsub.publish(["NEW_DEVICE"], { newDevice: device }); }) .on("newProperty", (property) => { if ( property.getType() === Indi.PropertyType.Number || property.getType() === Indi.PropertyType.Switch || property.getType() === Indi.PropertyType.Text || property.getType() === Indi.PropertyType.Light ) { pubsub.publish(["NEW_PROPERTY"], { newProperty: property }); } }) .on("newNumber", (n) => { pubsub.publish(["NEW_VALUE"], { newValue: n }); }) .on("newSwitch", (s) => { pubsub.publish(["NEW_VALUE"], { newValue: s }); }) .on("newText", (t) => { pubsub.publish(["NEW_VALUE"], { newValue: t }); }) .on("newLight", (l) => { pubsub.publish(["NEW_VALUE"], { newValue: l }); }) .on("newBLOB", (b) => { console.log("new blob " + b.length); fs.writeFile("test.fits", b, (err) => { if (err) { console.log("unable to save buffer"); return; } console.log("test.fits saved"); }); //pubsub.publish(["NEW_BLOB"], { newValue: l }); }) .on("newMessage", (device, id) => { console.log(device.messageQueue(id)); pubsub.publish(["NEW_MESSAGE"], { newMessage: { id: device.getDeviceName() + "." + id, device, message: device.messageQueue(id), }, }); }); // Resolvers const resolvers = { Query: { connected() { return indi.connected; }, device(_, { name }) { return indi.getDevice(name); }, devices() { return indi.getDevices(); }, }, Mutation: { connect() { return indi.connect(); }, disconnect() { return indi.disconnect(); }, connectDevice(_, { id }) { return indi.connectDevice(id).then(() => indi.getDevice(id)); }, sendNumber(_, { input: { device, property, values } }) { const dev = indi.getDevice(device); const props = dev.getProperty(property); if (!props) { return false; } const vector = props.getValue(); for (let i = 0; i < vector.values.length; i++) { vector.values[i].value = values[i].value; } return indi .sendNewNumber(vector) .then(() => true) .catch(() => false); }, sendOneNumber(_, { input: { device, property, name, value } }) { return indi .sendNewNumber(device, property, name, value) .then(() => true) .catch(() => false); }, sendSwitch(_, { input: { device, property, values } }) { const dev = indi.getDevice(device); const props = dev.getProperty(property); if (!props) { return false; } const vector = props.getValue(); for (let i = 0; i < vector.values.length; i++) { vector.values[i].value = values[i].value; } return indi .sendNewSwitch(vector) .then(() => true) .catch(() => false); }, sendOneText(_, { input: { device, property, name, value } }) { return indi .sendNewText(device, property, name, value) .then(() => true) .catch(() => false); }, }, Subscription: { connected: { subscribe: () => pubsub.asyncIterator(["CONNECTED"]), }, disconnected: { subscribe: () => pubsub.asyncIterator(["DISCONNECTED"]), }, newDevice: { subscribe: () => pubsub.asyncIterator(["NEW_DEVICE"]), }, newProperty: { subscribe: () => pubsub.asyncIterator(["NEW_PROPERTY"]), }, newValue: { subscribe: () => pubsub.asyncIterator(["NEW_VALUE"]), }, newMessage: { subscribe: () => pubsub.asyncIterator(["NEW_MESSAGE"]), }, }, Device: { id(device) { return device.getDeviceName(); }, name(device) { return device.getDeviceName(); }, properties(device) { return device .getProperties() .filter( (p) => p.getType() === Indi.PropertyType.Number || p.getType() === Indi.PropertyType.Text || p.getType() === Indi.PropertyType.Switch || p.getType() === Indi.PropertyType.Light ); }, drivers(device) { return device.getDriverInterface(); }, }, Property: { id(property) { return property.getDeviceName() + "." + property.getName(); }, name(property) { return property.getName(); }, vector(property) { return property.getValue(); }, device(property) { return property.getDeviceName(); }, permission(property) { return property.getPermission(); }, }, Vector: { __resolveType(value) { const dev = indi.getDevice(value.device); const props = dev.getProperty(value.name); switch (props.getType()) { case Indi.PropertyType.Number: return "NumberVector"; case Indi.PropertyType.Switch: return "SwitchVector"; case Indi.PropertyType.Text: return "TextVector"; case Indi.PropertyType.Light: return "LightVector"; default: return null; } }, }, NumberVector: { id(vector) { return vector.device + "." + vector.name; }, }, TextVector: { id(vector) { return vector.device + "." + vector.name; }, }, SwitchVector: { id(vector) { return vector.device + "." + vector.name; }, }, LightVector: { id(vector) { return vector.device + "." + vector.name; }, }, }; const server = new ApolloServer({ typeDefs, resolvers }); //indi.connect().then(() => console.log("INDI connected")); // The `listen` method launches a web server. server.listen().then(({ url, subscriptionsUrl }) => { console.log(`🚀 Server ready at ${url} pubsub=${subscriptionsUrl}`); });