Skip to content

Commit 87d9ba8

Browse files
authored
Add custom Node-RED nodes (#621)
After a bit of playing around we decided to start using custom Node-RED Nodes instead of creating a new server or adding APIs to device-backend. These will be used by * first time setup * dashboard 2 * portal (replaced with dashboard 2)
1 parent ef731f9 commit 87d9ba8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+7053
-44
lines changed

.vscode/extensions.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
3+
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
4+
5+
// List of extensions which should be recommended for users of this workspace.
6+
"recommendations": [
7+
"esbenp.prettier-vscode",
8+
"charliermarsh.ruff",
9+
"editorconfig.editorconfig",
10+
"dbaeumer.vscode-eslint"
11+
12+
],
13+
// List of extensions recommended by VSCodium that should not be recommended for users of this workspace.
14+
"unwantedRecommendations": [
15+
16+
]
17+
}

documentation/docs/community/contribute/tips-and-tricks.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ Make sure to replace `$planktoscope` with your PlanktoScope hostname, eg. `pksco
5252
ssh-keygen -t ed25519 -C "pi@$planktoscope" -f ~/.ssh/$planktoscope
5353
# Make the SSH key accepted by the PlanktoScope
5454
ssh-copy-id -i ~/.ssh/$planktoscope.pub pi@$planktoscope
55+
# Add your keys to your SSH agent
56+
ssh-add -k
5557
```
5658

5759
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
options cfg80211 ieee80211_regdom=

software/distro/setup/planktoscope-app-env/PlanktoScope/install.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,7 @@ sudo bash -c "cat \"$config_files_root/config.txt.snippet\" >> \"/boot/firmware/
1717
# Disable the 4 Raspberry logo in the top left corner
1818
# more space for kernel and system logs
1919
sudo sed -i -e 's/$/ logo.nologo/' /boot/firmware/cmdline.txt
20+
21+
# Create a file needed by the "set country" Node-RED node
22+
sudo cp -r "$config_files_root/cfg80211_regdomain.conf" "/etc/modprobe.d/"
23+
sudo chown -R pi:pi "/etc/modprobe.d/cfg80211_regdomain.conf"

software/distro/setup/planktoscope-app-env/node-red-frontend/install.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ if ! sudo apt-get install -y python3-smbus2; then
2323
pip3 install smbus2==0.4.3
2424
fi
2525

26-
# Install Node.js 20
27-
# https://github.com/nodesource/distributions/blob/master/README.md#using-debian-as-root-nodejs-20
28-
curl -fsSL https://deb.nodesource.com/setup_20.x -o /tmp/nodesource_setup.sh
26+
# Install Node.js 22
27+
# https://github.com/nodesource/distributions/blob/master/README.md#using-debian-as-root-nodejs-22
28+
curl -fsSL https://deb.nodesource.com/setup_22.x -o /tmp/nodesource_setup.sh
2929
sudo -E bash /tmp/nodesource_setup.sh
3030
sudo apt-get install -y nodejs
3131

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
*.backup
22
.config.*.json
3+
/planktoscopehat/context
4+
/planktoscopehat/projects
5+
flows_cred.json
Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
{
2-
"name": "planktoscope-node-red-dashboard",
3-
"description": "PlanktoScope graphical user interface",
4-
"dependencies": {
5-
"@flowfuse/node-red-dashboard": "^1.22.1",
6-
"@flowfuse/node-red-dashboard-2-ui-flowviewer": "^1.0.1",
7-
"node-red-contrib-cron-plus": "^2.1.0",
8-
"node-red-contrib-dir2files": "^0.3.0",
9-
"node-red-contrib-gpsd": "^1.0.4",
10-
"node-red-contrib-python3-function": "^0.0.4",
11-
"node-red-contrib-ui-multistate-switch": "^1.2.2",
12-
"node-red-dashboard": "^3.1.2",
13-
"node-red-node-pi-gpio": "^2.0.1",
14-
"node-red-node-ui-list": "^0.3.6"
15-
},
16-
"node-red": {
17-
"settings": {
18-
"flowFile": "flows.json",
19-
"credentialsFile": "credentials.json"
20-
}
2+
"name": "planktoscope-node-red-dashboard",
3+
"description": "PlanktoScope graphical user interface",
4+
"dependencies": {
5+
"@flowfuse/node-red-dashboard": "^1.22.1",
6+
"@flowfuse/node-red-dashboard-2-ui-flowviewer": "^1.0.1",
7+
"node-red-contrib-cron-plus": "^2.1.0",
8+
"node-red-contrib-dir2files": "^0.3.0",
9+
"node-red-contrib-gpsd": "^1.0.4",
10+
"node-red-contrib-python3-function": "^0.0.4",
11+
"node-red-contrib-ui-multistate-switch": "^1.2.2",
12+
"node-red-dashboard": "^3.1.2",
13+
"node-red-node-pi-gpio": "^2.0.1",
14+
"node-red-node-ui-list": "^0.3.6"
15+
},
16+
"node-red": {
17+
"settings": {
18+
"flowFile": "flows.json",
19+
"credentialsFile": "flows_cred.json"
2120
}
21+
}
2222
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# nodes
2+
3+
Custom Node-RED modules for the PlanktoScope
4+
5+
- https://nodered.org/docs/creating-nodes/
6+
- https://github.com/node-red/node-red-node-test-helper/
7+
- https://nodejs.org/docs/latest-v20.x/api/test.html
8+
- https://nodejs.org/docs/latest-v20.x/api/assert.html
9+
10+
## Development
11+
12+
```sh
13+
cd nodes
14+
npm install
15+
npm test
16+
```
17+
18+
Run excotaxa:
19+
20+
https://github.com/ecotaxa/ecotaxa_front/tree/master/docker/all_in_one
21+
22+
Note:
23+
24+
On Fedora you need to disable SELinux with
25+
26+
```sh
27+
sudo setenforce 0
28+
```
29+
30+
Start node-red manually
31+
32+
```sh
33+
sudo systemctl stop node-red
34+
node-red --settings /home/pi/PlanktoScope/software/node-red-dashboard/settings.js
35+
```
36+
37+
## Tests
38+
39+
Currently broken
40+
41+
```
42+
node --test ecotaxa.test.js
43+
```
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { readFile, writeFile } from "fs/promises"
2+
3+
// We don't use files from "sudo dpkg-query -L wireless-regdb"
4+
// because the tool to read them crda was removed in Bookworm
5+
export async function getWifiRegulatoryDomains() {
6+
const iso3166tab = await readFile("/usr/share/zoneinfo/iso3166.tab", {
7+
encoding: "utf8",
8+
})
9+
let countries = []
10+
for (const line of iso3166tab.split("\n")) {
11+
if (line.startsWith("#")) continue
12+
const [value, label] = line.split("\t")
13+
if (value && label) {
14+
countries.push({ value, label })
15+
}
16+
}
17+
countries = countries.sort((a, b) => {
18+
return a.label.localeCompare(b.label)
19+
})
20+
21+
return countries
22+
}
23+
24+
const path = "/etc/modprobe.d/cfg80211_regdomain.conf"
25+
26+
// We don't use `iw reg get` and `iw reg set`
27+
// because
28+
// * it is temporary
29+
// * wifi needs to be disconnected for get to reflect set
30+
export async function setWifiRegulatoryDomain(code) {
31+
await writeFile(path, `options cfg80211 ieee80211_regdom=${code}`)
32+
}
33+
34+
// TODO: set by default to 00 which is global setting
35+
export async function getWifiRegulatoryDomain() {
36+
let data
37+
38+
try {
39+
data = await readFile(path, {
40+
encoding: "utf8",
41+
})
42+
} catch (err) {
43+
if (err.code !== "ENOENT") throw err
44+
data = ""
45+
}
46+
47+
const [, code] = data.match(/options cfg80211 ieee80211_regdom=(.*$)/)
48+
return code || null
49+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { openAsBlob } from "node:fs"
2+
import { extname, basename } from "node:path"
3+
4+
import mime from "mime"
5+
6+
export async function login({ api_url, username, password }) {
7+
const req = await fetch(new URL("login", api_url), {
8+
method: "POST",
9+
headers: {
10+
"Content-Type": "application/json",
11+
},
12+
body: JSON.stringify({ username, password }),
13+
})
14+
const token = await req.json()
15+
return token
16+
}
17+
18+
// async function createProject({api_url, token, title }) {
19+
// const req = await fetch(new URL("projects/create", api_url), {
20+
// method: "POST",
21+
// headers: {
22+
// "Content-Type": "application/json",
23+
// Authorization: `Bearer ${token}`,
24+
// },
25+
// body: JSON.stringify({
26+
// // "clone_of_id": 2,
27+
// title: "My new project title",
28+
// // "instrument": "PlanktoScope test",
29+
// // "visible": True,
30+
// }),
31+
// })
32+
// const project_id = await req.json()
33+
// return project_id
34+
// }
35+
36+
export async function uploadFile({ api_url, token, path }) {
37+
const type = mime.getType(extname(path))
38+
const blob = await openAsBlob(path, { type })
39+
const file = new File([blob], basename(path), { type: blob.type })
40+
41+
const form = new FormData()
42+
form.append("file", file)
43+
44+
const req = await fetch(new URL("my_files/", api_url), {
45+
method: "POST",
46+
headers: {
47+
Authorization: `Bearer ${token}`,
48+
},
49+
body: form,
50+
})
51+
const res = await req.json()
52+
return res
53+
}
54+
55+
export async function importFile({ api_url, token, project_id, path }) {
56+
const req = await fetch(new URL(`file_import/${project_id}`, api_url), {
57+
method: "POST",
58+
headers: {
59+
Authorization: `Bearer ${token}`,
60+
"Content-Type": "application/json",
61+
},
62+
body: JSON.stringify({
63+
source_path: path,
64+
// "taxo_mappings": {"23444": 76543},
65+
// "skip_loaded_files": False,
66+
// "skip_existing_objects": False,
67+
// "update_mode": "Yes",
68+
}),
69+
})
70+
71+
const result = await req.json()
72+
return result
73+
}

0 commit comments

Comments
 (0)