Last active
August 3, 2022 14:56
-
-
Save niksm7/dc2173e31624079295329cebc3d33f54 to your computer and use it in GitHub Desktop.
This file contains the code for the ThingSpeak Pack which integrates functionality of ThingSpeak platform with coda
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as coda from "@codahq/packs-sdk"; | |
export const pack = coda.newPack(); | |
// Allow requests to the thingspeak API. | |
pack.addNetworkDomain("thingspeak.com"); | |
// Setup per-user api key to be used in making requests | |
pack.setUserAuthentication({ | |
type: coda.AuthenticationType.Custom, | |
params: [ | |
{ name: "user_api_key", description: "The API key" }, | |
], | |
instructionsUrl: "https://thingspeak.com/account/profile" | |
}); | |
// Schema for sync table that displays channels | |
const ChannelSchema = coda.makeObjectSchema({ | |
properties: { | |
id: { type: coda.ValueType.Number }, | |
name: { type: coda.ValueType.String }, | |
description: { type: coda.ValueType.String }, | |
latitude: { type: coda.ValueType.String }, | |
longitude: { type: coda.ValueType.String }, | |
created_at: { type: coda.ValueType.String, codaType: coda.ValueHintType.DateTime }, | |
elevation: { type: coda.ValueType.String }, | |
last_entry_id: { type: coda.ValueType.Number }, | |
public_flag: { type: coda.ValueType.Boolean }, | |
url: { type: coda.ValueType.String }, | |
ranking: { type: coda.ValueType.Number }, | |
metadata: { type: coda.ValueType.String }, | |
license_id: { type: coda.ValueType.Number }, | |
github_url: { type: coda.ValueType.String }, | |
tags: { | |
type: coda.ValueType.Array, | |
items: coda.makeSchema({ type: coda.ValueType.String }), | |
}, | |
api_keys: { | |
type: coda.ValueType.Array, | |
items: coda.makeSchema({ type: coda.ValueType.String }) | |
} | |
}, | |
displayProperty: "id", | |
idProperty: "id", | |
featuredProperties: ["name", "description", "created_at", "last_entry_id", "public_flag", "api_keys"], | |
}) | |
// Schema for sync table displaying channel data | |
const ChannelDataSchema = coda.makeObjectSchema({ | |
properties: { | |
channel: { | |
type: coda.ValueType.String, | |
}, | |
feeds: { | |
type: coda.ValueType.Array, | |
items: coda.makeSchema({ type: coda.ValueType.String }), | |
}, | |
}, | |
displayProperty: "channel", | |
idProperty: "channel", | |
featuredProperties: [], | |
}) | |
// Schema of Alert history sync table | |
const AlertHistorySchema = coda.makeObjectSchema({ | |
properties: { | |
requestedAt: { | |
type: coda.ValueType.String, | |
codaType: coda.ValueHintType.DateTime | |
}, | |
sentAt: { | |
type: coda.ValueType.String, | |
codaType: coda.ValueHintType.DateTime | |
}, | |
subject: { | |
type: coda.ValueType.String, | |
}, | |
status: { | |
type: coda.ValueType.String, | |
}, | |
}, | |
displayProperty: "requestedAt", | |
idProperty: "requestedAt", | |
featuredProperties: ["requestedAt", "sentAt", "subject", "status"], | |
}) | |
// List of common parameters that are used at different places | |
let common_parameters = [ | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "ChannelId", | |
description: "Channel ID for the channel of interest.", | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "FieldId", | |
description: "Field ID for the field of interest.", | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "results", | |
description: "Number of entries to retrieve. The maximum number is 8000. The default is 100.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "days", | |
description: "Number of 24-hour periods before now to include in feed.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Date, | |
name: "start", | |
description: "Start date in format YYYY-MM-DD%20HH:NN:SS.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Date, | |
name: "end", | |
description: "End date in format YYYY-MM-DD%20HH:NN:SS.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "offset", | |
description: "Timezone offset used to display results. Use the timezone parameter for greater accuracy.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "min", | |
description: "Minimum value to include in response.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "max", | |
description: "Maximum value to include in response.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "round", | |
description: "Round to this many decimal places.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "timescale", | |
description: "Get first value in this many minutes. The valid values are: 10, 15, 20, 30, 60, 240, 720, 1440, 'daily'.", | |
optional: true, | |
autocomplete: ["10", "15", "20", "30", "60", "240", "720", "1440", "daily"] | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "sum", | |
description: "Get sum of this many minutes. The valid values are: 10, 15, 20, 30, 60, 240, 720, 1440, 'daily'.", | |
optional: true, | |
autocomplete: ["10", "15", "20", "30", "60", "240", "720", "1440", "daily"] | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "average", | |
description: "Get average of this many minutes. The valid values are 10, 15, 20, 30, 60, 240, 720, 1440, 'daily'. \ | |
Note: NaN values are interpreted as 0 when calculating the average.", | |
optional: true, | |
autocomplete: ["10", "15", "20", "30", "60", "240", "720", "1440", "daily"] | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "median", | |
description: "Get median of this many minutes. The valid values are: 10, 15, 20, 30, 60, 240, 720, 1440, 'daily'", | |
optional: true, | |
autocomplete: ["10", "15", "20", "30", "60", "240", "720", "1440", "daily"] | |
}), | |
] | |
// Function to get the channel API key | |
async function getChannelAPI(ChannelId, context) { | |
let invocationToken = context.invocationToken; | |
let user_api_key = "{{user_api_key-" + invocationToken + "}}"; | |
let response = await context.fetcher.fetch({ | |
method: "GET", | |
url: "https://api.thingspeak.com/channels/" + ChannelId + ".json?api_key=" + user_api_key, | |
cacheTtlSecs: 240 | |
}); | |
return response.body.api_keys[1].api_key; | |
} | |
// Function used to add query parameters to the url | |
function queryStringParameters(urls_string, results, days, start, end, offset, min, max, round, timescale, sum, average, median) { | |
if (results != undefined) { | |
urls_string += "&results=" + results | |
} | |
if (days != undefined) { | |
urls_string += "&days=" + days | |
} | |
if (start != undefined) { | |
urls_string += "&start=" + start | |
} | |
if (end != undefined) { | |
urls_string += "&end=" + end | |
} | |
if (offset != undefined) { | |
urls_string += "&offset=" + offset | |
} | |
if (min != undefined) { | |
urls_string += "&min=" + min | |
} | |
if (max != undefined) { | |
urls_string += "&max=" + max | |
} | |
if (round != undefined) { | |
urls_string += "&round=" + round | |
} | |
if (timescale != undefined) { | |
urls_string += "×cale=" + timescale | |
} | |
if (sum != undefined) { | |
urls_string += "&sum=" + sum | |
} | |
if (average != undefined) { | |
urls_string += "&average=" + average | |
} | |
if (median != undefined) { | |
urls_string += "&median=" + median | |
} | |
return urls_string | |
} | |
// Formula that generates charts for channels and allows customization | |
pack.addFormula({ | |
name: "CreateChart", | |
description: "Generate charts for different channels with customization.", | |
resultType: coda.ValueType.String, | |
schema: { | |
type: coda.ValueType.String, | |
codaType: coda.ValueHintType.Embed, | |
force: true, | |
}, | |
parameters: [ | |
common_parameters[0], | |
common_parameters[1], | |
coda.makeParameter({ | |
type: coda.ParameterType.Boolean, | |
name: "dynamic", | |
description: "Make chart update automatically based on the time interval mentioned in update. The default is false.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "update", | |
description: "Time interval in seconds to update the chart automatically. The default is 15.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "title", | |
description: "Chart title. The default is the channel name.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "xaxis_label", | |
description: "Chart x-axis label. The default is 'Date'.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "yaxis_label", | |
description: "Chart y-axis label. The default is the field name.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "color", | |
description: "Hex Value for line color. The default is FF000 (red).", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "bgcolor", | |
description: "Background color. The default is white.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "chart_type", | |
description: "Type of chart. The default is line.", | |
optional: true, | |
autocomplete: ["line", "bar", "column", "spline"], | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "width", | |
description: "Chart width in pixels, iframe width is 20 px larger, default chart width: 400. \ | |
Set to auto to automatically adjust chart size based on its parent container.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "height", | |
description: "Chart height in pixels, iframe height is 20 px larger, default chart height: 200. \ | |
Set to auto to automatically adjust chart size based on its parent container.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Boolean, | |
name: "step", | |
description: "Draw chart as a step chart. The default is false.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "yaxis_min", | |
description: "Minimum value of chart Y-Axis. If blank, the value is auto-calculated.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "yaxis_max", | |
description: "Maximum value of chart Y-Axis. If blank, the value is auto-calculated.", | |
optional: true, | |
}), | |
common_parameters[2], | |
common_parameters[3], | |
common_parameters[4], | |
common_parameters[5], | |
common_parameters[6], | |
common_parameters[7], | |
common_parameters[8], | |
common_parameters[9], | |
common_parameters[10], | |
common_parameters[11], | |
common_parameters[12], | |
common_parameters[13], | |
], | |
execute: async function ([ChannelId, FieldId, dynamic, update, title, xaxis_label, yaxis_label, color, bgcolor, chart_type, | |
width, height, step, yaxis_min, yaxis_max, results, days, start, end, offset, min, max, round, timescale, sum, average, median], context) { | |
let url_string = "https://api.thingspeak.com/channels/" + ChannelId + "/charts/" + FieldId + "?api_key=" + await getChannelAPI(ChannelId, context) | |
if (dynamic != undefined) { | |
url_string += "&dynamic=" + dynamic | |
} | |
if (update != undefined) { | |
url_string += "&update=" + update | |
} | |
if (title != undefined) { | |
url_string += "&title=" + title | |
} | |
if (xaxis_label != undefined) { | |
url_string += "&xaxis=" + xaxis_label | |
} | |
if (yaxis_label != undefined) { | |
url_string += "&yaxis=" + yaxis_label | |
} | |
if (color != undefined) { | |
url_string += "&color=" + color | |
} | |
if (bgcolor != undefined) { | |
url_string += "&bgcolor=" + bgcolor | |
} | |
if (chart_type != undefined) { | |
url_string += "&type=" + chart_type | |
} | |
if (width != undefined) { | |
url_string += "&width=" + width | |
} | |
if (height != undefined) { | |
url_string += "&height=" + height | |
} | |
if (step != undefined) { | |
url_string += "&step=" + step | |
} | |
if (yaxis_min != undefined) { | |
url_string += "&yaxismin=" + yaxis_min | |
} | |
if (yaxis_max != undefined) { | |
url_string += "&yaxismax=" + yaxis_max | |
} | |
url_string = queryStringParameters(url_string, results, days, start, end, offset, min, max, round, timescale, sum, average, median) | |
return url_string | |
}, | |
}); | |
// Formula that generates a channel map | |
pack.addFormula({ | |
name: "CreateChannelMap", | |
description: "Show channel location on map", | |
resultType: coda.ValueType.String, | |
schema: { | |
type: coda.ValueType.String, | |
codaType: coda.ValueHintType.Embed, | |
force: true, | |
}, | |
parameters: [ | |
common_parameters[0], | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "width", | |
description: "Map width in pixels. Default map width is 450.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "height", | |
description: "Chart height in pixels. Default map height is 260.", | |
optional: true, | |
}), | |
], | |
execute: async function ([ChannelId, width, height], context) { | |
let url_string = "https://thingspeak.com/channels/" + ChannelId + "/maps/channel_show?read_api_key=" + await getChannelAPI(ChannelId, context) | |
if (width != undefined) { | |
url_string += "&width=" + width | |
} | |
if (height != undefined) { | |
url_string += "&height=" + height | |
} | |
return url_string | |
} | |
}); | |
// Sync table that displays all the channels with their information | |
pack.addSyncTable({ | |
name: "Channels", | |
identityName: "Channels", | |
schema: ChannelSchema, | |
formula: { | |
name: "SyncChannels", | |
description: "List all your channels.", | |
parameters: [], | |
execute: async function ([], context) { | |
let invocationToken = context.invocationToken; | |
let user_api_key = "{{user_api_key-" + invocationToken + "}}"; | |
let url = "https://api.thingspeak.com/channels.json?api_key=" + user_api_key; | |
let response = await context.fetcher.fetch({ | |
method: "GET", | |
url: url, | |
cacheTtlSecs: 0, | |
}); | |
let items = response.body; | |
if (items != []) { | |
return { | |
result: items, | |
}; | |
} | |
else { | |
throw new coda.UserVisibleError("The table has no data to display!"); | |
} | |
} | |
} | |
}) | |
// Formula that updates the settings of a channel | |
pack.addFormula({ | |
name: "WriteChannelSetting", | |
description: "Update your channel settings", | |
resultType: coda.ValueType.String, | |
isAction: true, | |
parameters: [ | |
common_parameters[0], | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "name", | |
description: "Name of the channel.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "description", | |
description: "Description of the channel.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "url", | |
description: "Webpage URL for the channel.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "latitude", | |
description: "Latitude in degrees, specified as a value between -90 and 90.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "longitude", | |
description: "Longitude in degrees, specified as a value between -180 and 180.", | |
optional: true, | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "public_flag", | |
description: "Whether the channel is public. The default is false.", | |
optional: true, | |
}), | |
], | |
execute: async function ([ChannelId, name, description, url, latitude, longitude, public_flag], context) { | |
let url_string = "https://api.thingspeak.com/channels/" + ChannelId + ".json" | |
let invocationToken = context.invocationToken; | |
let user_api_key = "{{user_api_key-" + invocationToken + "}}"; | |
let payload = { | |
"api_key": user_api_key, | |
}; | |
if (name != undefined) { | |
payload["name"] = name | |
} | |
if (description != undefined) { | |
payload["description"] = description | |
} | |
if (url != undefined) { | |
payload["url"] = url | |
} | |
if (longitude != undefined) { | |
payload["longitude"] = String(longitude) | |
} | |
if (latitude != undefined) { | |
payload["latitude"] = String(latitude) | |
} | |
if (public_flag != undefined) { | |
payload["public_flag"] = public_flag | |
} | |
let response = await context.fetcher.fetch({ | |
method: "PUT", | |
url: url_string, | |
headers: { | |
"Content-Type": "application/x-www-form-urlencoded", | |
}, | |
form: payload | |
}); | |
if (response.status == 200) { | |
return "Updation successfull" | |
} | |
else { | |
return "Something went wrong!" | |
} | |
} | |
}); | |
// Sync table that displays data of a channel | |
pack.addSyncTable({ | |
name: "ChannelData", | |
identityName: "ChannelData", | |
schema: ChannelDataSchema, | |
dynamicOptions: { | |
getSchema: async function (context, _, { ChannelId }) { | |
let url_string = "https://api.thingspeak.com/channels/" + ChannelId + "/feeds.json?api_key=" + await getChannelAPI(ChannelId, context) | |
let response = await context.fetcher.fetch({ | |
method: "GET", | |
url: url_string, | |
cacheTtlSecs: 120, | |
}); | |
let projectMetadata = response.body.feeds[0]; | |
if (projectMetadata == undefined) { | |
throw new coda.UserVisibleError("The table has no data to display!"); | |
} | |
else { | |
let properties: coda.ObjectSchemaProperties = {}; | |
let featuredProperties = []; | |
for (const [name, value] of Object.entries(projectMetadata)) { | |
if (name == "entry_id") { | |
properties[name] = { | |
type: coda.ValueType.Number | |
}; | |
} | |
else { | |
properties[name] = { | |
type: coda.ValueType.String | |
}; | |
} | |
featuredProperties.push(name); | |
} | |
let displayProperty = "created_at"; | |
let idProperty = "entry_id"; | |
// Return the schema for each row. | |
return coda.makeObjectSchema({ | |
properties: properties, | |
displayProperty: displayProperty, | |
idProperty: idProperty, | |
featuredProperties: featuredProperties, | |
}); | |
} | |
}, | |
}, | |
formula: { | |
name: "SyncChannelData", | |
description: "Read data from all fields in channel.", | |
parameters: [ | |
common_parameters[0], | |
common_parameters[2], | |
common_parameters[3], | |
common_parameters[4], | |
common_parameters[5], | |
common_parameters[6], | |
common_parameters[7], | |
common_parameters[8], | |
common_parameters[9], | |
common_parameters[10], | |
common_parameters[11], | |
common_parameters[12], | |
common_parameters[13], | |
], | |
execute: async function ([ChannelId, results, days, start, end, offset, min, max, round, timescale, sum, average, median], context) { | |
let url_string = "https://api.thingspeak.com/channels/" + ChannelId + "/feeds.json?api_key=" + await getChannelAPI(ChannelId, context) | |
url_string = queryStringParameters(url_string, results, days, start, end, offset, min, max, round, timescale, sum, average, median) | |
let response = await context.fetcher.fetch({ | |
method: "GET", | |
url: url_string, | |
cacheTtlSecs: 0, | |
}); | |
let items = response.body.feeds; | |
if (items[0] != undefined) { | |
return { | |
result: items, | |
}; | |
} | |
else { | |
throw new coda.UserVisibleError("The table has no data to display!"); | |
} | |
} | |
} | |
}) | |
// Sync table that displays data of a particular field of a channel | |
pack.addSyncTable({ | |
name: "FieldData", | |
identityName: "FieldData", | |
schema: ChannelDataSchema, | |
dynamicOptions: { | |
getSchema: async function (context, _, { ChannelId, FieldId }) { | |
let url_string = "https://api.thingspeak.com/channels/" + ChannelId + "/fields/" + FieldId + ".json?api_key=" + await getChannelAPI(ChannelId, context) | |
let response = await context.fetcher.fetch({ | |
method: "GET", | |
url: url_string, | |
cacheTtlSecs: 120 | |
}); | |
let projectMetadata = response.body.feeds[0]; | |
if (projectMetadata == undefined) { | |
throw new coda.UserVisibleError("The table has no data to display!"); | |
} | |
else { | |
let properties: coda.ObjectSchemaProperties = {}; | |
let featuredProperties = []; | |
for (const [name, value] of Object.entries(projectMetadata)) { | |
if (name == "entry_id") { | |
properties[name] = { | |
type: coda.ValueType.Number | |
}; | |
} | |
else { | |
properties[name] = { | |
type: coda.ValueType.String | |
}; | |
} | |
featuredProperties.push(name); | |
} | |
let displayProperty = "created_at"; | |
let idProperty = "entry_id"; | |
// Return the schema for each row. | |
return coda.makeObjectSchema({ | |
properties: properties, | |
displayProperty: displayProperty, | |
idProperty: idProperty, | |
featuredProperties: featuredProperties, | |
}); | |
} | |
}, | |
}, | |
formula: { | |
name: "SyncFieldData", | |
description: "Read data from single field of channel.", | |
parameters: [ | |
common_parameters[0], | |
common_parameters[1], | |
common_parameters[2], | |
common_parameters[3], | |
common_parameters[4], | |
common_parameters[5], | |
common_parameters[6], | |
common_parameters[7], | |
common_parameters[8], | |
common_parameters[9], | |
common_parameters[10], | |
common_parameters[11], | |
common_parameters[12], | |
common_parameters[13], | |
], | |
execute: async function ([ChannelId, FieldId, results, days, start, end, offset, min, max, round, timescale, sum, average, median], context) { | |
let url_string = "https://api.thingspeak.com/channels/" + ChannelId + "/fields/" + FieldId + ".json?api_key=" + await getChannelAPI(ChannelId, context) | |
url_string = queryStringParameters(url_string, results, days, start, end, offset, min, max, round, timescale, sum, average, median) | |
let response = await context.fetcher.fetch({ | |
method: "GET", | |
url: url_string, | |
cacheTtlSecs: 0, | |
}); | |
let items = response.body.feeds; | |
return { | |
result: items, | |
}; | |
} | |
} | |
}) | |
// Formula that creates and sends email alerts | |
pack.addFormula({ | |
name: "CreateEmailAlert", | |
description: "Create an email alert", | |
resultType: coda.ValueType.String, | |
isAction: true, | |
parameters: [ | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "alert_api_key", | |
description: "Specify the alerts API key, which you can find in your profile (https://thingspeak.com/account/profile). \ | |
This key is different from the channel API and user API keys.", | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "subject", | |
description: "Specify the subject for the email message, up to 60 characters.", | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "body_description", | |
description: "Specify the body of the email message, up to 500 characters.", | |
}), | |
], | |
execute: async function ([alert_api_key, subject, body_description], context) { | |
if (alert_api_key != undefined && subject != undefined && body_description != undefined) { | |
let response = await context.fetcher.fetch({ | |
method: "POST", | |
headers: { | |
"Content-Type": "application/json", | |
"ThingSpeak-Alerts-API-Key": alert_api_key | |
}, | |
url: "https://api.thingspeak.com/alerts/send", | |
body: JSON.stringify({ | |
"subject": subject, | |
"body": body_description | |
}) | |
}); | |
if (response.status == 200) { | |
return "Updation successfull" | |
} | |
else { | |
return "Something went wrong!" | |
} | |
} | |
return "Waiting" | |
} | |
}) | |
// Sync table that displays all the alerts processed | |
pack.addSyncTable({ | |
name: "AlertHistory", | |
identityName: "AlertHistory", | |
schema: AlertHistorySchema, | |
formula: { | |
name: "SyncAlertHistory", | |
description: "List alerts history.", | |
parameters: [ | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "alert_api_key", | |
description: "Specify the alerts API key, which you can find in your profile (https://thingspeak.com/account/profile). \ | |
This key is different from the channel API and user API keys.", | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "count", | |
description: "The number of results to return. The default value is 10, the maximum value is 100.", | |
optional: true | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "subject_contains", | |
description: "Include only history items with subject containing this value, sensitive to case.", | |
optional: true | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "status", | |
description: "Include only history items with status containing this value.", | |
optional: true | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "after", | |
description: "Include only history items with requested_at later than this time. Format times per ISO 8601. \ | |
For example, 2020-05-15T20:03:48-05:00 represents May 15, 2020, 20:03:48 EST. \ | |
If you do not include the time zone offset, ThingSpeak assumes the specified time is in UTC. \ | |
Note: Alerts history items are retained for only 7 days, after which they are no longer available.", | |
optional: true | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "before", | |
description: "Include only history items with requested_at earlier than this time. Format times per ISO 8601. \ | |
For example, 2020-05-15T20:03:48-05:00 represents May 15, 2020, 20:03:48 EST. \ | |
If you do not include the time zone offset, ThingSpeak assumes the specified time is in UTC. \ | |
Note: Alerts history items are retained for only 7 days, after which they are no longer available.", | |
optional: true | |
}), | |
], | |
execute: async function ([alert_api_key, count, subject_contains, status, after, before], context) { | |
let url_string = "https://api.thingspeak.com/alerts/history"; | |
let added_initial_parameter = false | |
if (count != undefined) { | |
if (!added_initial_parameter) { | |
url_string += "?count=" + count | |
} | |
else { | |
url_string += "&count=" + count | |
} | |
} | |
if (subject_contains != undefined) { | |
if (!added_initial_parameter) { | |
url_string += "?subject_contains=" + subject_contains | |
added_initial_parameter = true | |
} | |
else { | |
url_string += "&subject_contains=" + count | |
} | |
} | |
if (status != undefined) { | |
if (!added_initial_parameter) { | |
url_string += "?status=" + status | |
added_initial_parameter = true | |
} | |
else { | |
url_string += "&status=" + status | |
} | |
} | |
if (after != undefined) { | |
if (!added_initial_parameter) { | |
url_string += "?after=" + after | |
added_initial_parameter = true | |
} | |
else { | |
url_string += "&after=" + after | |
} | |
} | |
if (before != undefined) { | |
if (!added_initial_parameter) { | |
url_string += "?before=" + before | |
added_initial_parameter = true | |
} | |
else { | |
url_string += "&before=" + before | |
} | |
} | |
let response = await context.fetcher.fetch({ | |
method: "GET", | |
headers: { | |
"ThingSpeak-Alerts-API-Key": alert_api_key | |
}, | |
url: url_string, | |
cacheTtlSecs: 0, | |
}); | |
let items = response.body; | |
if (items[0] == undefined) { | |
throw new coda.UserVisibleError("The table has no data to display!"); | |
} | |
else { | |
return { | |
result: items, | |
}; | |
} | |
} | |
} | |
}) | |
// Formula that creates a new channel | |
pack.addFormula({ | |
name: "CreateChannel", | |
description: "Create a new channel.", | |
isAction: true, | |
resultType: coda.ValueType.String, | |
parameters: [ | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "name", | |
description: "Name of the channel.", | |
optional: true | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "description", | |
description: "Specify the description for the channel.", | |
optional: true | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "latitude", | |
description: "Latitude in degrees.", | |
optional: true | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.Number, | |
name: "longitude", | |
description: "Longitude in degrees.", | |
optional: true | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "public_flag", | |
description: "Whether the channel is public. The default is false.", | |
optional: true | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "metadata", | |
description: "Metadata for the channel, which can include JSON, XML, or any other data.", | |
optional: true | |
}), | |
coda.makeParameter({ | |
type: coda.ParameterType.String, | |
name: "url", | |
description: "Web page URL for the channel.", | |
optional: true | |
}), | |
], | |
execute: async function ([name, description, latitude, longitude, public_flag, metadata, url], context) { | |
let invocationToken = context.invocationToken; | |
let user_api_key = "{{user_api_key-" + invocationToken + "}}"; | |
let payload = { | |
"api_key": user_api_key, | |
}; | |
if (name != undefined) { | |
payload["name"] = name | |
} | |
if (description != undefined) { | |
payload["description"] = description | |
} | |
if (url != undefined) { | |
payload["url"] = url | |
} | |
if (longitude != undefined) { | |
payload["longitude"] = String(longitude) | |
} | |
if (latitude != undefined) { | |
payload["latitude"] = String(latitude) | |
} | |
if (public_flag != undefined) { | |
payload["public_flag"] = public_flag | |
} | |
if (metadata != undefined) { | |
payload["metadata"] = metadata | |
} | |
let response = await context.fetcher.fetch({ | |
method: "POST", | |
url: "https://api.thingspeak.com/channels.json", | |
headers: { | |
"Content-Type": "application/x-www-form-urlencoded", | |
}, | |
form: payload | |
}); | |
if (response.status == 200) { | |
return "Channel Created!" | |
} | |
else if (response.status == 402) { | |
return "You have exceeded channel limit!" | |
} | |
else { | |
return "Something went wrong!" | |
} | |
} | |
}) | |
// Formula that clears the data of a channel | |
pack.addFormula({ | |
name: "ClearChannel", | |
description: "Clears all the data from a channel", | |
resultType: coda.ValueType.String, | |
isAction: true, | |
parameters: [ | |
common_parameters[0], | |
], | |
execute: async function ([ChannelId], context) { | |
let invocationToken = context.invocationToken; | |
let user_api_key = "{{user_api_key-" + invocationToken + "}}"; | |
if (ChannelId != undefined) { | |
let response = await context.fetcher.fetch({ | |
method: "DELETE", | |
url: "https://api.thingspeak.com/channels/" + ChannelId + "/feeds.json", | |
headers: { | |
"Content-Type": "application/x-www-form-urlencoded", | |
}, | |
form: { | |
api_key: user_api_key | |
} | |
}); | |
if (response.status == 200) { | |
return "Channel Cleared!" | |
} | |
else { | |
return "Something went wrong!" | |
} | |
} | |
else { | |
return "Waiting!" | |
} | |
} | |
}) | |
// Formula that deletes a Channel | |
pack.addFormula({ | |
name: "DeleteChannel", | |
description: "Delete a channel.", | |
resultType: coda.ValueType.String, | |
isAction: true, | |
parameters: [ | |
common_parameters[0], | |
], | |
execute: async function ([ChannelId], context) { | |
let invocationToken = context.invocationToken; | |
let user_api_key = "{{user_api_key-" + invocationToken + "}}"; | |
if (ChannelId != undefined) { | |
let response = await context.fetcher.fetch({ | |
method: "DELETE", | |
url: "https://api.thingspeak.com/channels/" + ChannelId + ".json", | |
headers: { | |
"Content-Type": "application/x-www-form-urlencoded", | |
}, | |
form: { | |
api_key: user_api_key | |
} | |
}); | |
if (response.status == 200) { | |
return "Channel Deleted!" | |
} | |
else { | |
return "Something went wrong!" | |
} | |
} | |
else { | |
return "Waiting!" | |
} | |
} | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment