Registered StackHub users may elect to receive email notifications whenever a new package version is released or a comment is posted on the forum.
There are 0 watchers.
Following is a more complex example of Axon functions that power an EasyConn connector.
Similar to the more basic example in the documentation these functions connect to the HTTP REST API provided by the UK's NationalGrid to obtain carbon intensity values for the electrical power generation in all regions of the UK.
This specific example, connecting to the UK's National Grid, was chosen because the data is publicly available and the REST API does NOT require any form of authentication.
Most REST APIs will require some form of authentication, for which Fantom Factory's HTTP Client SkySpark extension offers a secure means to send authentication credentials.
Most of the code in these Axon functions deal with the specifics of transforming the data structures returned from the National Grid API - in many cases the actual data returned does not match the documentation!
On the EasyConn connector record, you would add the following tags:
afEasyConnExtLearnFn : "ciLearn" afEasyConnExtSyncCurFn : "ciSyncCur" afEasyConnExtSyncHisFn : "ciSyncHis"
Learning is often the most complex function of a connector. These 3 functions provide a complete heirarical Learn tree for Carbon Intensity data.
// func// name: ciLearn(conn, arg : null) => do if (arg == null) return [ { dis:"National", learn:"/national" }, { dis:"Regional", learn:"/regional" }, ].toGrid() uri : arg.parseUri path : uri.uriPath if (path.size == 1 and path[0] == "national") return [ { dis:"Intensity", learn:"/national/intensity" }, { dis:"Statistics", learn:"/national/statistics" }, { dis:"Generation Mix", learn:"/national/generation" }, ].toGrid() if (path.size == 1 and path[0] == "regional") return [ { dis:"Kingdoms", learn:"/regional/kingdoms" }, { dis:"Regions", learn:"/regional/regions" }, ].toGrid() if (path.size == 2) return ciLearnPath2(uri) if (path.size == 3) return ciLearnPath3(uri) return null end
// func// name: ciLearnPath2(arg) => do path : arg.uriPath if (path[1] == "intensity") do json : ioReadJson(`https://api.carbonintensity.org.uk/intensity`, {safeNames}) dataDict : json["data"].getSafe(0) if (dataDict == null) return null intDict : dataDict["intensity"] if (intDict == null) return null learnItems : [] intDict.each() (val, key) => do kind : "Number" if (key == "index") kind = "Str" learnItems = learnItems.add({ "dis" : key.capitalize, "kind" : kind, "ciKey" : key, "ciType" : "intensity", his, cur, ciNational, }) end return learnItems.toGrid end if (path[1] == "statistics") do from : (now() - 30min).format("YYYY-MM-DD'T'hh:mmz") to : now().format("YYYY-MM-DD'T'hh:mmz") json : ioReadJson(parseUri("https://api.carbonintensity.org.uk/intensity/stats/" + from + "/" + to)) dataDict : json["data"].getSafe(0) if (dataDict == null) return null statDict : dataDict["intensity"] if (statDict == null) return null learnItems : [] statDict.each() (val, key) => do kind : "Number" if (key == "index") kind = "Str" learnItems = learnItems.add({ "dis" : key.capitalize, "kind" : kind, "ciKey" : key, "ciType" : "statistics", his, cur, ciNational, }) end return learnItems.toGrid end if (path[1] == "generation") do json : ioReadJson(`https://api.carbonintensity.org.uk/generation`, {safeNames})// For some reason this is coming back as a single dict even though api specifies a listdataDict : json["data"] if (dataDict == null) return null genDict : dataDict["generationmix"] if (genDict == null) return null learnItems : [] genDict.each() (dict) => do learnItems = learnItems.add({ "dis" : dict->fuel.capitalize, "kind" : "Number", "ciKey" : dict->fuel, "ciType" : "generation", "unit" : "%", his, cur, ciNational, }) end return learnItems.toGrid end if (path[1] == "kingdoms") return [ {dis:"England", learn:"/regional/kingdoms/england"}, {dis:"Wales", learn:"/regional/kingdoms/wales"}, {dis:"Scotland", learn:"/regional/kingdoms/scotland"} ].toGrid() if (path[1] == "regions") do json : ioReadJson(`https://api.carbonintensity.org.uk/regional`, {safeNames}) dataDict : json["data"].getSafe(0) if (dataDict == null) return null regionDict : dataDict["regions"] if (regionDict == null) return null learnItems : [] regionDict.each() (dict) => do learn : arg.uriPathStr + "/" + dict->regionid.format("0") learnItems = learnItems.add({ "dis" : dict->shortname.capitalize, "learn" : learn, "shortname" : dict->shortname, "dnoregion" : dict->dnoregion, }) end return learnItems.toGrid end return null end
// func// name: ciLearnPath3(arg) => do path : arg.uriPath if (path[1] == "kingdoms") do url : parseUri(`https://api.carbonintensity.org.uk/regional/`.toStr() + path[2]) json : ioReadJson(url, {safeNames}) dataDict : json["data"].getSafe(0) if (dataDict == null) return null// another odd layout compared to documented apiregionDict : dataDict.get("data").getSafe(0) if (regionDict == null) return null learnItems : [] regionDict->intensity.each() (val, key) => do kind : "Number" if (key == "index") kind = "Str" learnItems = learnItems.add({ "dis" : key.capitalize + " (Intensity)", "ciRegionId" : dataDict->regionid, "ciKey" : key, "ciType" : "intensity", "kind" : kind, his, cur, }) end regionDict->generationmix.each() (dict) => do learnItems = learnItems.add({ "dis" : dict->fuel.capitalize + " (Generation Mix)", "ciRegionId" : dataDict->regionid, "ciKey" : dict->fuel, "ciType" : "generation", "kind" : "Number", "unit" : "%", his, cur, }) end return learnItems.toGrid end if (path[1] == "regions") do url : parseUri(`https://api.carbonintensity.org.uk/regional/regionid/`.toStr() + path[2]) json : ioReadJson(url, {safeNames}) dataDict : json["data"].getSafe(0) if (dataDict == null) return null regionDict : dataDict.get("data").getSafe(0)// another odd layout compared to documented apiif (regionDict == null) return null learnItems : [] regionDict->intensity.each() (val, key) => do addr : parseUri(arg.uriPathStr + "?key=" + key) kind : "Number" if (key == "index") kind = "Str" learnItems = learnItems.add({ "dis" : key.capitalize + " (Intensity)", "ciRegionId" : dataDict->regionid, "ciKey" : key, "ciType" : "intensity", "kind" : kind, his, cur, }) end regionDict->generationmix.each() (dict) => do learnItems = learnItems.add({ "dis" : dict->fuel.capitalize + " (Generation Mix)", "ciRegionId" : dataDict->regionid, "ciKey" : dict->fuel, "ciType" : "generation", "kind" : "Number", "unit" : "%", his, cur, }) end return learnItems.toGrid end return null end
The ciSyncCur
function clamps the current time to a 30 minute interval and calls out to the REST API, using identifying data saved in the point record.
// func// name: ciSyncCur(conn, points) => do hour : now().time.hour mins : now().time.minute if (mins < 30) mins = 0 else mins = 30 from : dateTime(today(), time(hour, mins )).format("YYYY-MM-DD'T'hh:mmz") to : dateTime(today(), time(hour, mins+29)).format("YYYY-MM-DD'T'hh:mmz") return points.map() (point) => do if (point["ciNational"] != null) do if (point["ciType"] == "generation") do json : ioReadJson(`https://api.carbonintensity.org.uk/generation`, {safeNames}) genDict : json["data"]["generationmix"].find(dict => dict["fuel"] == point["ciKey"]) return {pointRef:point->id, val:genDict["perc"]} end if (point["ciType"] == "statistics") do json : ioReadJson(parseUri("https://api.carbonintensity.org.uk/intensity/stats/" + from + "/" + to), {safeNames}) statsDict : json["data"][0]["intensity"] return {pointRef:point->id, val:statsDict[point["ciKey"]]} end if (point["ciType"] == "intensity") do json : ioReadJson(`https://api.carbonintensity.org.uk/intensity`, {safeNames}) intDict : json["data"][0]["intensity"] return {pointRef:point->id, val:intDict[point["ciKey"]]} end end else do json : ioReadJson(parseUri("https://api.carbonintensity.org.uk/regional/regionid/" + point["ciRegionId"].toStr), {safeNames}) if (point["ciType"] == "intensity") do intDict : json["data"][0]["data"][0]["intensity"] return {pointRef:point->id, val:intDict[point["ciKey"]]} end if (point["ciType"] == "generation") do genDict : json["data"][0]["data"][0]["generationmix"].find(dict => dict["fuel"] == point["ciKey"]) return {pointRef:point->id, val:genDict["perc"]} end end throw "Something went wrong - ensure tags in bound points are correctly configured." end.toGrid() end
The ciSyncHis
function returns time series data from the REST API for the given point and date span.
// func// name: ciSyncHis(conn, point, span) => do point = point.toRec hour : span.start.time.hour mins : span.start.time.minute if (mins < 30) mins = 0 else mins = 30 from : dateTime(span.start.date(), time(hour, mins)) spanDur : span.end - span.start minsDur : spanDur.to(1min).round numSegs : minsDur / 30 hisItems : [] numSegs.times() (i) => do tempFrom : from .format("YYYY-MM-DD'T'hh:mmz") tempTo : (from + 29min).format("YYYY-MM-DD'T'hh:mmz") if ((from + 29min) >= now()) return null if (point["ciNational"] != null) do if (point["ciType"] == "generation") do json : ioReadJson(parseUri("https://api.carbonintensity.org.uk/generation/" + tempFrom + "/" + tempTo), {safeNames}) genDict : json["data"][0]["generationmix"].find(dict => dict["fuel"] == point["ciKey"]) hisItems = hisItems.add({ts:from, val:genDict["perc"]}) end else if (point["ciType"] == "statistics") do json : ioReadJson(parseUri("https://api.carbonintensity.org.uk/intensity/stats/" + tempFrom + "/" + tempTo), {safeNames}) statsDict : json["data"][0]["intensity"] hisItems = hisItems.add({ts:from, val:statsDict[point["ciKey"]]}) end else if (point["ciType"] == "intensity") do json : ioReadJson(parseUri("https://api.carbonintensity.org.uk/intensity/" + tempFrom + "/" + tempTo), {safeNames}) intDict : json["data"][0]["intensity"] hisItems = hisItems.add({ts:from, val:intDict[point["ciKey"]]}) end end else do json : ioReadJson(parseUri("https://api.carbonintensity.org.uk/regional/intensity/" + tempFrom + "/" + tempTo + "/regionid/" + point["ciRegionId"].toStr), {safeNames}) if (point["ciType"] == "intensity") do intDict : json["data"]["data"][0]["intensity"] hisItems = hisItems.add({ts:from, val:intDict[point["ciKey"]]}) end else if (point["ciType"] == "generation") do genDict : json["data"]["data"][0]["generationmix"].find(dict => dict["fuel"] == point["ciKey"]) hisItems = hisItems.add({ts:from, val:genDict["perc"]}) end end from = from + 30min end return hisItems.toGrid end
Version | 1.0.0 |
---|---|
License | Commercial |
Build date | 1 year ago on 26th May 2023 |
Requirements | SkySpark v3.1.6 |
Depends on | |
File name | afEasyConnExt.pod |
File size | 157.51 kB |
MD5 | 36f87e2ed50b45dc730d85941b3a8168 |
SHA1 | 5e5cd9c2041abca27e4bf84e3b67a393e424eff9 |
Published by Fantom FactoryDownload nowAlso available via SkyArc Install Manager |