#!/bin/bash # # Node-RED Installer for Debian based systems # # Copyright 2016,2026 JS Foundation and other contributors, https://js.foundation/ # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Default Node.js major version to install if none is present or --nodeXX flag is used NODE_VERSION=24 # Pinned Node.js v22 build for ARMv6 (Pi Zero/1) - no v24+ builds exist for this arch tgta22=22.23.1 # armv6l latest from https://unofficial-builds.nodejs.org/download/release/ ARMV6="n" if [ ! -f /etc/debian_version ] || [ "$(uname)" == "Darwin" ]; then echo " " echo "************************************************************" echo " " echo " Apologies... " echo " This installer is only for Debian based Linux systems. " echo " Please see the documentation at " echo " http://nodered.org/docs/getting-started/upgrading " echo " " echo -e "************************************************************\n" exit 1 elif [[ "$(uname -m)" =~ "i686" ]] || [[ "$(uname -m)" =~ "x86_64" && "$(getconf LONG_BIT)" =~ "32" ]]; then echo " " echo "************************************************************" echo " " echo " STOP ! " echo " There are no known Node.js v24 builds for 32 bit systems. " echo " " echo -e "************************************************************\n" exit 1 elif uname -m | grep -q armv6l ; then echo " " echo "************************************************************" echo " " echo " Warning : " echo " There are no v24 or later builds for Arm v6 systems. " echo " Using Node.js v22 instead. " echo " Node.js v22 goes EOL in April 2027. " echo " " echo "************************************************************" sleep 6 NODE_VERSION=22 ARMV6="y" fi umask 0022 usage() { cat << EOL Usage: install-update-nodered [options] Options: --help display this help and exit --confirm-root install as root without asking confirmation --confirm-install confirm installation without asking confirmation --confirm-pi confirm installation of PI specific nodes without asking confirmation --skip-pi skip installing PI specific nodes without asking confirmation --update-nodes run npm update on existing installed nodes (within scope of package.json) --allow-low-ports add capability to bind to ports below 1024 (default is disallow) --no-init don't ask to initialise settings if they don't exist --on-diet bypass DietPi exit and force install Node-RED on DietPi --nodered-user specify the user to run as, useful for installing as sudo - e.g. --nodered-user=pi --nodered-version if not set, the latest version is used - e.g. --nodered-version="5.0.0" --node22 if set, forces install of latest major version of nodejs 22 LTS --node24 if set, forces install of latest major version of nodejs 24 LTS --node26 if set, forces install of latest major version of nodejs 26 LTS if none set, install nodejs 24 LTS if no nodejs is installed otherwise leave current nodejs install --clean force overwrite of .node-red directory, systemd script, settings.js, nodejs Note: if you use allow-low-ports it may affect the node modules paths - see https://stackoverflow.com/a/65560687 EOL } SUDO=sudo SUDOE="sudo -E" LOW_PORTS="n" if [ $# -gt 0 ]; then # Parsing parameters while (( "$#" )); do case "$1" in --help) usage && exit 0 shift ;; --confirm-root) CONFIRM_ROOT="y" shift ;; --confirm-install) CONFIRM_INSTALL="y" shift ;; --skip-pi) CONFIRM_PI="n" shift ;; --confirm-pi) CONFIRM_PI="y" shift ;; --update-nodes) UPDATENODES="y" shift ;; --nodered-version=*) NODERED_VERSION="${1#*=}" shift ;; --nodered-user=*) NODERED_USER="${1#*=}" shift ;; --node22) NODE_VERSION="22" FORCE_NODE="y" shift ;; --node24) NODE_VERSION="24" FORCE_NODE="y" shift ;; --node26) NODE_VERSION="26" FORCE_NODE="y" shift ;; --allow-low-ports) LOW_PORTS="y" shift ;; --no-init) INITSET="n" shift ;; --on-diet) ONDIET="y" shift ;; --clean) FORCE_INSTALL="y" # delete existing .node-red FORCE_NODE="y" # [re]install node.js if [[ "$INITSET" != "n" ]] ; then INITSET="y" # run initial setup script fi shift ;; --) # end argument parsing shift break ;; -*|--*=) # unsupported flags echo "Error: Unsupported flag $1" >&2 exit 1 ;; esac done if [[ "$ARMV6" == "y" ]] && [ "$NODE_VERSION" -gt 22 ]; then echo "Armv6l platform detected. Option --node$NODE_VERSION is not supported. Exiting." echo " " exit 1 fi fi # helper function to test for existance of node and npm function HAS_NODE { if [ -x "$(command -v node)" ]; then return 0; else return 1; fi } function HAS_NPM { if [ -x "$(command -v npm)" ]; then return 0; else return 1; fi } # check for apt and systemctl (set flags for later use and log if not found) if [ -x "$(command -v apt)" ]; then APTOK=true; else APTOK=false echo "apt not found. Node/Npm install will be skipped" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null fi if [ -x "$(command -v systemctl)" ]; then SYSTEMDOK=true; else SYSTEMDOK=false echo "systemctl not found. shortcuts/services setup will be skipped" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null fi echo -ne "\033[2 q" if [[ -e /mnt/dietpi_userdata && "$ONDIET" != "y" ]]; then echo -ne "\n\033[1;32mDietPi\033[0m detected - only going to add the \033[0;36mnode-red-start, -stop, -log\033[0m commands.\n" echo -ne "Flow files and other things worth backing up can be found in the \033[0;36m/mnt/dietpi_userdata/node-red\033[0m directory.\n\n" echo -ne "Use the \033[0;36mdietpi-software\033[0m command to un-install and re-install \033[38;5;88mNode-RED\033[0m.\n\n" echo -ne "If you want to force install Node-RED, re-run this script with the \033[0;36m--on-diet\033[0m option.\n\n" echo "journalctl -f -n 100 -u node-red -o cat" > /usr/bin/node-red-log chmod +x /usr/bin/node-red-log echo "systemctl stop node-red" > /usr/bin/node-red-stop chmod +x /usr/bin/node-red-stop echo "systemctl start node-red" > /usr/bin/node-red-start echo "journalctl -f -n 0 -u node-red -o cat" >> /usr/bin/node-red-start chmod +x /usr/bin/node-red-start exit 0 fi if [[ -e /mnt/dietpi_userdata && "$ONDIET" == "y" ]]; then DIETPI="y" echo -ne "\n\033[1;32mDietPi\033[0m detected but going to force install of Node-RED.\n" echo -ne "\nThis should only be done if you DO NOT use the dietpi-software\ninstaller to set up Node.js and Node-RED.\n" echo -ne "\nTrying to mix both will possibly break something - Use at your own risk.\n\n" read -t 10 -p "Are you really sure you want to force install on DietPi ? (y/N) ? " yn case $yn in [Yy]* ) ;; * ) echo " " exit 1 ;; esac echo -ne "\nOK - fingers crossed.\n\n" apt install -y python3-rpi.gpio 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null CONFIRM_ROOT="y" CONFIRM_INSTALL="y" fi if [ "$EUID" == "0" ]; then echo -en "\nroot user detected. Typical installs should be done as a regular user.\r\n" echo -en "If you are running this script using sudo, please cancel and rerun without sudo.\r\n" echo -en "--nodered-user can be used to specify the user otherwise installation will happen under /root.\r\n" echo -en "If you know what you are doing as root, please continue.\r\n\r\n" yn="${CONFIRM_ROOT}" [ ! "${yn}" ] && read -t 10 -p "Are you really sure you want to install as root ? (y/N) ? " yn case $yn in [Yy]* ) NODERED_USER="root" ;; * ) echo " " exit 1 ;; esac SUDO='' SUDOE='' id -u nobody &>/dev/null || adduser --no-create-home --shell /dev/null --disabled-password --disabled-login --gecos '' nobody &>/dev/null else groups "$USER" | grep -q '\bsudo\b' && GRS="Y" || GRS="N" if [[ "$GRS" == "N" ]]; then echo "User $USER not in sudoers group. Exiting" exit 1; fi fi # setup user, home and group if [[ "$NODERED_USER" == "" ]]; then NODERED_HOME=$HOME NODERED_USER=$USER NODERED_GROUP=$(id -gn) else NODERED_GROUP="$NODERED_USER" NODERED_HOME="/home/$NODERED_USER" if [[ "$NODERED_USER" == "root" ]]; then NODERED_HOME="/root" fi fi SUDOU="sudo -u $NODERED_USER" curl -I https://registry.npmjs.org/@node-red/util >/dev/null 2>&1 CURL_RC=$? if [ $CURL_RC -eq 0 ]; then echo " " echo Node-RED update script for : $NODERED_USER@$(hostname) echo " " echo "This script checks if Node.js is installed, and will try to install" echo "Node.js $NODE_VERSION if it is not found." echo " " echo "It will then remove the old core of Node-RED, before then installing the latest" echo "version. You can also optionally specify the version required." echo " " echo "To do all this it runs commands as root - please satisfy yourself that this will" echo "not damage your Pi, or otherwise compromise your configuration." echo " " echo "If in doubt please backup your SD card first." echo " " echo "See the optional parameters by re-running this command with --help" echo " " if [[ -e "$NODERED_HOME/.nvm" ]]; then echo -ne '\033[1mNOTE:\033[0m We notice you are using \033[38;5;88mnvm\033[0m.\n' echo -ne 'Please ensure it is running the current LTS version.\n' echo -ne 'Using nvm is NOT RECOMMENDED. Node-RED will not run as a service under nvm.\r\n\n' sleep 7 elif [[ $(which n) ]]; then echo -ne '\033[1mNOTE:\033[0m We notice you are using \033[38;5;88mn\033[0m.\n' echo -ne 'Please ensure it is running the current LTS version.\n' echo -ne 'Using n is NOT RECOMMENDED.\r\n\n' sleep 7 fi if HAS_NODE && HAS_NPM; then nv2=$(node -v 2>/dev/null) np2=$(npm -v 2>/dev/null) echo "Node.js $nv2 and Npm $np2 already installed" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null echo -ne "Node.js $nv2 and Npm $np2 already installed.\n\n" # Decide whether the currently installed Node.js needs upgrading # because it is older than the minimum supported version of 22.9.0 NV_MAJOR=$(echo "$nv2" | sed 's/^v//' | cut -d '.' -f1) NV_MINOR=$(echo "$nv2" | sed 's/^v//' | cut -d '.' -f2) NEEDS_UPGRADE="n" if [[ "$NV_MAJOR" -lt 22 ]] || { [[ "$NV_MAJOR" -eq 22 ]] && [[ "${NV_MINOR:-0}" -lt 9 ]]; }; then NEEDS_UPGRADE="y" fi if [[ "$NEEDS_UPGRADE" == "y" && "$FORCE_NODE" != "y" ]]; then echo "Installed nodejs $nv2 is below the minimum of 22.9.0 - will remove and install new version" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null echo " " echo "Node-RED requires Nodejs 22.9.0 or later - you have $nv2" echo "Will attempt to remove the existing Node.js and install $NODE_VERSION." echo " " FORCE_NODE="y" fi else echo "Node.js and Npm not found - will try to install Node.js $NODE_VERSION" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null echo -ne "Node.js and Npm not found - will try to install Node.js $NODE_VERSION\n\n" fi if [[ "$FORCE_INSTALL" == "y" ]] ; then echo " " echo "********************************************************************************" echo "** **" echo "** Warning : **" echo "** The --clean option will COMPLETELY OVERWRITE your .node-red directory **" echo "** **" echo "** This will remove existing projects **" echo "** flows **" echo "** installed nodes **" echo "** users **" echo "** settings file **" echo "** and other customisation **" echo "** **" echo "********************************************************************************" fi yn="${CONFIRM_INSTALL}" [ ! "${yn}" ] && read -t 30 -p "Are you really sure you want to do this ? [y/N] ? " yn case $yn in [Yy]* ) echo "" EXTRANODES="" EXTRAW="update" response="${CONFIRM_PI}" [ ! "${response}" ] && read -r -t 15 -p "Would you like to install the Pi-specific nodes ? [y/N] ? " response if [[ "$response" =~ ^([yY])+$ ]]; then # Extra nodes installed on Raspberry Pi only (see Pi-specific section below) EXTRANODES="node-red-node-pi-gpio@latest node-red-node-random@latest node-red-node-ping@latest node-red-contrib-play-audio@latest node-red-node-smooth@latest node-red-node-serialport@latest node-red-contrib-buffer-parser@latest" EXTRAW="install" fi # this script assumes that $HOME is the folder of the user that runs node-red # that $NODERED_USER is the user name and the group name to use when running is the # primary group of that user # if this is not correct then edit the lines below MYOS=$(grep "^ID=" /etc/os-release | cut -d = -f 2 | tr -d '"') GLOBAL="true" TICK='\033[1;32m\u2714\033[0m' CROSS='\033[1;31m\u2718\033[0m' cd "$NODERED_HOME" || exit 1 clear echo -e "\nRunning \033[38;5;88mNode-RED\033[0m install for user $NODERED_USER at $NODERED_HOME on $MYOS\n" time1=$(date) if [[ "$FORCE_NODE" == "y" ]]; then echo " Removing any existing Node.js and npm... " echo "Force node install option selected - will try to remove existing nodejs and npm first" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO apt -yq purge nodejs npm 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO rm -rf /usr/local/bin/node* /usr/local/lib/node_modules/npm* /usr/bin/node* /usr/lib/node_modules/npm* 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null fi NUPG="" if HAS_NODE && HAS_NPM; then nv2=$(node -v 2>/dev/null) np2=$(npm -v 2>/dev/null) if [[ "$ARMV6" == "y" ]]; then echo " There are no v24 or later builds for Arm v6 systems " echo " -- Node.js v22 goes End Of Life in April 2027 -- " fi # Check for nvm or n usage. if [[ -d "$NODERED_HOME/.nvm" ]]; then GLOBAL="false" echo -ne ' Using NVM to manage Node.js + please run \033[0;36mnvm use lts\033[0m before running Node-RED\r\n' echo -ne ' NOTE: Using nvm is NOT RECOMMENDED. Node-RED will NOT run as a service under nvm.\r\n' export NVM_DIR="$NODERED_HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm echo "Using NVM !!! $(nvm current)" 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null nvm install $NODE_VERSION --no-progress --latest-npm >/dev/null 2>&1 nvm use $NODE_VERSION >/dev/null 2>&1 nvm alias default $NODE_VERSION >/dev/null 2>&1 echo "Now using --- $(nvm current)" 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null ln -f -s "$NODERED_HOME/.nvm/versions/node/$(nvm current)/lib/node_modules/node-red/red.js" "$NODERED_HOME/node-red" echo -ne " Update Node.js $NODE_VERSION $CHAR - Using nvm is NOT recommended/supported." elif [[ $(which n) ]]; then echo "Using n" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null echo -ne " Using N to manage Node.js + $nv2\r\n" # echo -ne ' NOTE: Using n is NOT RECOMMENDED. Attempting to set version to '$NODE_VERSION'.\r\n' # $SUDO n $NODE_VERSION 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null # if [ ${PIPESTATUS[0]} -eq 0 ]; then CHAR=$TICK; else CHAR=$CROSS; fi # echo -ne " Update Node.js $NODE_VERSION $CHAR - Using n is NOT recommended/supported.\n" fi else # Now install nodejs and npm if [[ "$ARMV6" == "y" ]]; then echo " Armv6l platform detected - will try to install Node.js $tgta22" echo -ne " \033[1mNOTE:\033[0m Node.js v22 goes EOL in April 2027\r\n This platform will not be supported after that date\r\n\r\n" echo -ne " Install Node.js $tgta22 for Armv6l \033[5m_\033[0m" curl -sSL -o "$NODERED_HOME/node.tgz" https://unofficial-builds.nodejs.org/download/release/v$tgta22/node-v$tgta22-linux-armv6l.tar.gz 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null # unpack it into the correct places hd=$(head -c 6 "$NODERED_HOME/node.tgz") if [ "$hd" == "" ]; then CHAR="$CROSS node-v${tgta22}-linux-armv6l.tar.gz not downloaded"; else if [[ -d /tmp/nodejs ]]; then $SUDO rm -rf /tmp/nodejs fi mkdir -p /tmp/nodejs $SUDO tar -zxof "$NODERED_HOME/node.tgz" --strip-components=1 -C /tmp/nodejs $SUDO chown -R 0:0 /tmp/nodejs 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO cp -PR /tmp/nodejs/* /usr/ 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null if [ ${PIPESTATUS[0]} -eq 0 ]; then CHAR=$TICK; else CHAR=$CROSS; fi $SUDO rm -rf /tmp/nodejs 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null fi # remove the tgz file to save space rm "$NODERED_HOME/node.tgz" 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null nv2=$(node -v) np2=$(npm -v) NUPG=$CHAR echo -ne "\b $CHAR $nv2 : Npm $np2\r\n" else if [[ "$APTOK" == "false" ]]; then if HAS_NODE; then echo -en "\b$CROSS MISSING: Npm\r\n"; fi if HAS_NPM; then echo -en "\b$CROSS MISSING: Node.js\r\n"; fi echo -en "\b$CROSS MISSING: apt" echo -e "\r\n\r\nThis script uses apt to install Node.js and Npm.\n" echo -e "You can install Node.js and Npm manually then run the script again to continue.\r\n\r\n" exit 2 fi # Install nodejs for all other platforms echo " Will try to install Node.js $NODE_VERSION" echo -ne " Install Node.js \033[5m_\033[0m" # block debian nodejs install echo "Package: nodejs" | $SUDO tee /etc/apt/preferences.d/nodejs.pref >>/dev/null echo "Pin: release a=stable" | $SUDO tee -a /etc/apt/preferences.d/nodejs.pref >>/dev/null echo "Pin-Priority: -1" | $SUDO tee -a /etc/apt/preferences.d/nodejs.pref >>/dev/null # use the official script to install for other debian platforms $SUDO rm -rf /etc/apt/sources.list.d/nodesource.list 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO rm -rf /etc/apt/sources.list.d/nodesource.sources 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO apt install -y ca-certificates curl gnupg 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO mkdir -p /etc/apt/keyrings 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | $SUDOE gpg --batch --yes --dearmor -o /etc/apt/keyrings/nodesource.gpg 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" | $SUDOE tee -a /etc/apt/sources.list.d/nodesource.list >>/dev/null $SUDO apt-get update 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO apt install -y --reinstall nodejs 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null # echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" | $SUDOE tee -a /etc/apt/sources.list.d/nodesource.list >>/dev/null # $SUDO apt-get update 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null # $SUDO apt install -y --reinstall nsolid 2>&1 | $SUDO tee -a /var/log/nodered-install.log >> /dev/null if [ ${PIPESTATUS[0]} -eq 0 ]; then CHAR=$TICK; else CHAR=$CROSS; fi NUPG=$CHAR if [[ "$CHAR" == "$CROSS" ]]; then echo -ne "\b\ $CHAR Failed to install Node.js $NODE_VERSION\r\n" echo -ne " Please try installing Node.js $NODE_VERSION manually and then re-run this script.\r\n" exit 2 fi nv2=$(node -v) np2=$(npm -v) echo -ne "\b $CHAR $nv2 : Npm $np2\r\n" fi fi # OK - now we have node and npm - either existing or new - so we can try to install Node-RED # ensure ~/.config dir is owned by the user $SUDO chown -Rf "$NODERED_USER:$NODERED_GROUP" "$NODERED_HOME/.config/" # save the existing state of the service so we can restart it at the end if it was running before if [[ "$SYSTEMDOK" == "true" ]]; then if systemctl is-active --quiet nodered; then WAS_RUNNING="y"; else WAS_RUNNING="n"; fi else WAS_RUNNING="n" fi # stop any running node-red service if [[ "$WAS_RUNNING" == "y" ]]; then # echo " Node-RED service is currently running - will try to stop it before updating" $SUDO service nodered stop 2>&1 | $SUDO tee -a /var/log/nodered-install.log >> /dev/null if [ ${PIPESTATUS[0]} -eq 0 ]; then CHAR=$TICK; else CHAR=$CROSS; fi echo -ne " Stop Node-RED $CHAR\r\n" fi if [[ "$FORCE_INSTALL" == "y" ]] ; then # delete .node-red echo " Removing any existing .node-red directory... " $SUDO rm -rf "$NODERED_HOME/.node-red" 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null fi # remove any old node-red installs or files $SUDO rm -rf /usr/local/lib/node_modules/node-red* /usr/local/bin/node-red* 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO rm -rf /usr/lib/node_modules/node-red* /usr/bin/node-red* 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null echo -ne ' Remove old version of Node-RED \033[1;32m\u2714\033[0m\r\n' hash -r if [[ "$GLOBAL" == "true" ]]; then $SUDO npm cache clean --force 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null else npm cache clean --force 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null fi if $SUDO rm -rf "$NODERED_HOME/.node-gyp" "$NODERED_HOME/.npm" /root/.node-gyp /root/.npm; then CHAR=$TICK; else CHAR=$CROSS; fi echo -ne " Clean npm cache $CHAR\r\n" # and install Node-RED echo "Now install Node-RED $NODERED_VERSION" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null NODERED_VERSION_SELECTION="" if [ -z "${NODERED_VERSION}" ]; then NODERED_VERSION_SELECTION="latest" else NODERED_VERSION_SELECTION=${NODERED_VERSION} fi echo -ne " Install Node-RED core \033[5m_\033[0m " if [[ "$GLOBAL" == "true" ]]; then $SUDO npm i -g --no-progress --no-update-notifier --no-audit --no-fund --loglevel=error node-red@"$NODERED_VERSION_SELECTION" 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null; nri=${PIPESTATUS[0]} if [[ $nri -eq 0 ]]; then CHAR=$TICK; else CHAR=$CROSS; fi else npm i -g --no-progress --no-update-notifier --no-audit --no-fund --loglevel=error node-red@"$NODERED_VERSION_SELECTION" 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null; nri=${PIPESTATUS[0]} if [[ $nri -eq 0 ]]; then CHAR=$TICK; else CHAR=$CROSS; fi fi nrv=$(npm -g --no-progress --no-update-notifier --no-audit --no-fund --loglevel=error ls node-red | grep node-red | cut -d '@' -f 2 | $SUDO tee -a /var/log/nodered-install.log) >>/dev/null 2>&1 echo -ne "\b\b $CHAR $nrv\r\n" # Setup basic user dir - install some nodes if necesssary. echo "Now create basic package.json for the user" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null mkdir -p "$NODERED_HOME/.node-red/node_modules" $SUDO chown "$NODERED_USER:$NODERED_GROUP" "$NODERED_HOME/.node-red" 2>&1 >>/dev/null $SUDO chown -Rf "$NODERED_USER:$NODERED_GROUP" "$NODERED_HOME/.node-red/node_modules" 2>&1 >>/dev/null pushd "$NODERED_HOME/.node-red" 2>&1 >>/dev/null npm config set update-notifier false 2>&1 >>/dev/null npm config set fund false 2>&1 >>/dev/null if [ ! -f "package.json" ]; then cat > package.json << EOF { "name": "node-red-project", "description": "initially created for you by Node-RED $nrv", "version": "0.0.1", "private": true, "dependencies": { } } EOF fi # try to rebuild any already installed nodes CHAR="-" if [[ "$NUPG" == "$TICK" ]]; then echo -ne "Running npm rebuild\r\n" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null npm rebuild --no-progress --no-update-notifier --no-fund 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null if [ ${PIPESTATUS[0]} -eq 0 ]; then CHAR=$TICK; else CHAR=$CROSS; fi echo -ne " Npm rebuild existing nodes $CHAR\r" else echo -ne " Leave existing nodes -\r" fi if [[ "$UPDATENODES" == "y" ]]; then echo -ne "Running npm update\r\n" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null echo -ne " Npm update existing nodes " npm update --no-progress --no-update-notifier --no-fund 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null if [ ${PIPESTATUS[0]} -eq 0 ]; then CHAR=$TICK; else CHAR=$CROSS; fi echo -ne "$CHAR\r" fi echo -ne "\n" if [[ -n "$EXTRANODES" ]]; then npm i --save --no-progress --no-update-notifier --no-audit --no-fund $EXTRANODES 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null fi # If armv6 then remove the bcrypt binary to workaround illegal instruction error if uname -m | grep -q armv6l ; then $SUDO rm -rf /usr/lib/node_modules/node-red/node_modules/@node-rs 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null fi popd 2>&1 >>/dev/null if [ -d "$NODERED_HOME/.npm" ]; then $SUDO chown -Rf "$NODERED_USER:$NODERED_GROUP" "$NODERED_HOME/.npm" 2>&1 >>/dev/null fi if [[ "$SYSTEMDOK" == "true" ]] && [[ "$GLOBAL" == "true" ]]; then # add the shortcut and start/stop/log scripts to the menu echo "Now add the shortcut and start/stop/log scripts to the menu" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO mkdir -p /usr/bin if $SUDO curl -m 60 -f https://raw.githubusercontent.com/node-red/linux-installers/master/resources/node-red-icon.svg >/dev/null 2>&1; then $SUDO curl -sL -m 60 -o /usr/bin/node-red-start https://raw.githubusercontent.com/node-red/linux-installers/master/resources/node-red-start 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO curl -sL -m 60 -o /usr/bin/node-red-stop https://raw.githubusercontent.com/node-red/linux-installers/master/resources/node-red-stop 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO curl -sL -m 60 -o /usr/bin/node-red-restart https://raw.githubusercontent.com/node-red/linux-installers/master/resources/node-red-restart 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO curl -sL -m 60 -o /usr/bin/node-red-reload https://raw.githubusercontent.com/node-red/linux-installers/master/resources/node-red-reload 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO curl -sL -m 60 -o /usr/bin/node-red-log https://raw.githubusercontent.com/node-red/linux-installers/master/resources/node-red-log 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO curl -sL -m 60 -o /etc/logrotate.d/nodered https://raw.githubusercontent.com/node-red/linux-installers/master/resources/nodered.rotate 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO chmod +x /usr/bin/node-red-start $SUDO chmod +x /usr/bin/node-red-stop $SUDO chmod +x /usr/bin/node-red-restart $SUDO chmod +x /usr/bin/node-red-reload $SUDO chmod +x /usr/bin/node-red-log $SUDO curl -sL -m 60 -o /usr/share/icons/hicolor/scalable/apps/node-red-icon.svg https://raw.githubusercontent.com/node-red/linux-installers/master/resources/node-red-icon.svg 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO curl -sL -m 60 -o /usr/share/applications/Node-RED.desktop https://raw.githubusercontent.com/node-red/linux-installers/master/resources/Node-RED.desktop 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null echo -ne " Add shortcut commands $TICK\r\n" else echo -ne " Add shortcut commands $CROSS\r\n" fi # add systemd script and configure it for $NODERED_USER echo "Now add systemd script and configure it for $NODERED_USER:$NODERED_GROUP @ $NODERED_HOME" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null SYSTEMDFILE="/lib/systemd/system/nodered.service" $SUDO curl -sfL -m 60 -o "${SYSTEMDFILE}.temp" https://raw.githubusercontent.com/node-red/linux-installers/master/resources/nodered.service 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null if [ ${PIPESTATUS[0]} -eq 0 ]; then CHAR=$TICK; else CHAR=$CROSS; fi # set memory limit based on total RAM: <894MB->256, <1894MB->512, <3894MB->1024, <7894MB->2048, else->4096 TOTAL_MEM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2}') if [ "$TOTAL_MEM_KB" -lt 894000 ]; then mem="256" elif [ "$TOTAL_MEM_KB" -lt 1894000 ]; then mem="512" elif [ "$TOTAL_MEM_KB" -lt 3894000 ]; then mem="1024" elif [ "$TOTAL_MEM_KB" -lt 7894000 ]; then mem="2048" else mem="4096" fi $SUDO sed -i "s#=512#=${mem}#;" "${SYSTEMDFILE}.temp" $SUDO sed -i "s#^User=.*#User=${NODERED_USER}#;s#^Group=.*#Group=${NODERED_GROUP}#;s#^WorkingDirectory=.*#WorkingDirectory=${NODERED_HOME}#;s#^EnvironmentFile=-.*#EnvironmentFile=-${NODERED_HOME}/.node-red/environment#;" "${SYSTEMDFILE}.temp" grep -sq XDG_RUNTIME_DIR "$NODERED_HOME/.node-red/environment" || echo XDG_RUNTIME_DIR=/run/user/$(id -u "$NODERED_USER") >> "$NODERED_HOME/.node-red/environment" if test -f "$SYSTEMDFILE"; then if [[ "$FORCE_INSTALL" == "y" ]] ; then # Overwrite systemd script $SUDO cp "${SYSTEMDFILE}.temp" "$SYSTEMDFILE" fi # there's already a systemd script - compare checksums to detect customisation EXISTING_FILE=$(md5sum "$SYSTEMDFILE" | cut -d ' ' -f 1) TEMP_FILE=$(md5sum "${SYSTEMDFILE}.temp" | cut -d ' ' -f 1) if [[ "$EXISTING_FILE" == "$TEMP_FILE" ]]; then : # silent procedure - files are identical else echo "Customized systemd script found @ $SYSTEMDFILE. To prevent loss of modifications, we will not recreate the systemd script." | $SUDO tee -a /var/log/nodered-install.log >>/dev/null echo "If you want the installer to recreate the systemd script, please delete or rename the current script & re-run the installer." | $SUDO tee -a /var/log/nodered-install.log >>/dev/null CHAR="- Skipped - existing script is customized." fi $SUDO rm "${SYSTEMDFILE}.temp" else $SUDO mv "${SYSTEMDFILE}.temp" "$SYSTEMDFILE" fi $SUDO systemctl daemon-reload 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null echo -ne " Update systemd script $CHAR\r\n" else echo -ne " Add shortcut commands : Skipped - systemd not found\r\n" echo -ne " Update systemd script : Skipped - systemd not found\r\n" fi $SUDO ln -sf "$(which python3)" /usr/bin/python 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null CHAR=$TICK # remove unneeded large sentiment library to save space and load time $SUDO rm -f /usr/lib/node_modules/node-red/node_modules/multilang-sentiment/build/output/build-all.json 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null # on LXDE add launcher to top bar, refresh desktop menu pfile="/home/$NODERED_USER/.config/lxpanel/LXDE-pi/panels/panel" if [ -e "$pfile" ]; then if ! grep -q "Node-RED" "$pfile"; then mat="lxterminal.desktop" ins="lxterminal.desktop\n }\n Button {\n id=Node-RED.desktop" $SUDO sed -i "s|$mat|$ins|" "$pfile" 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null if xhost >& /dev/null ; then export DISPLAY=:0 && lxpanelctl restart 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null fi fi echo -ne " Add launcher to top bar $CHAR\r\n" fi # on Pi, add launcher to top bar, add cpu temp example, make sure ping works echo "Now add launcher to top bar, add cpu temp example, make sure ping works" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null if $SUDO grep -q Raspberry /proc/cpuinfo; then $SUDO adduser "$NODERED_USER" gpio 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null FAM=$(cut -d ' ' -f 1 /etc/issue) # OS family name, e.g. "Raspbian" or "Debian" ISS=$(cut -d '/' -f 2 /etc/issue | cut -d ' ' -f 2) # OS family name, e.g. "Raspbian" or "Debian" if [[ "$FAM" == *"bian" ]]; then if [ "$ISS" -gt 11 ] && [ "$DIETPI" != "y" ]; then echo "Replace old rpi.gpio with lgpio" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO apt purge -y python3-rpi.gpio 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO apt install -y python3-rpi-lgpio 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null if [[ -n "$EXTRANODES" ]]; then echo "Installing extra Pi nodes: $EXTRANODES :" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null echo -ne " Installing Extra Pi nodes " npm i --save --no-progress --no-update-notifier --no-audit --no-fund $EXTRANODES 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null echo -ne " $CHAR\r\n" fi else echo "Leaving old rpi.gpio" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO apt install -y python3-rpi.gpio 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null fi fi echo -ne " Fixup Pi tweaks $CHAR\r\n" fi $SUDO setcap cap_net_raw=ep /bin/ping 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null echo "Allow binding to low ports : $LOW_PORTS" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null if [[ "$LOW_PORTS" == "y" ]] ; then $SUDO setcap cap_net_bind_service=+ep "$(readlink -f "$(which node)")" 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null fi echo -ne " All done.\r\n\r\n" if [[ "$FORCE_INSTALL" == "y" ]] ; then # Don't restart node-red NB it does leave status "enabled" WAS_RUNNING="n" fi if [[ "$WAS_RUNNING" == "y" ]]; then echo -ne "\033[1mRestarting \033[38;5;88mNode-RED\033[0m service\r\n" $SUDO systemctl restart nodered echo -ne "\033[1mRestarted \033[38;5;88mNode-RED\033[0m\r\n" echo "Started : $time1 " | $SUDO tee -a /var/log/nodered-install.log echo "Finished: $(date)" | $SUDO tee -a /var/log/nodered-install.log echo " " else if [[ "$GLOBAL" == "true" ]] ; then if [[ "$SYSTEMDOK" == "true" ]]; then echo -ne "You can now start Node-RED with the command \033[0;36mnode-red-start\033[0m\r\n" echo -ne " or using the icon under Menu / Programming / Node-RED\r\n" else echo -ne "You can now start Node-RED with the command \033[0;36mnode-red\033[0m\r\n" fi else echo -ne "You can now start Node-RED with the command \033[0;36m./node-red\033[0m\r\n" fi echo -ne "Then point your browser to \033[0;36mlocalhost:1880\033[0m or \033[0;36mhttp://{your_pi_ip-address}:1880\033[0m\r\n" echo -ne "\r\n" if free -h -t >/dev/null 2>&1; then echo "Memory : $(free -h -t | grep Total | awk '{print $2}' | cut -d i -f 1)" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null else echo "Mem : $(free -m | grep Mem | awk '{print $2}' | cut -d i -f 1)Mb" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null echo "Swap : $(free -m | grep Swap | awk '{print $2}' | cut -d i -f 1)Mb" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null fi echo "Started : $time1 " | $SUDO tee -a /var/log/nodered-install.log echo "Finished: $(date)" | $SUDO tee -a /var/log/nodered-install.log file="/home/$NODERED_USER/.node-red/settings.js" if [[ "$NODERED_USER" == "root" ]]; then file=/root/.node-red/settings.js fi if [ ! -f "$file" ]; then echo " " elif ! diff -q -w --suppress-common-lines /usr/lib/node_modules/node-red/settings.js "$file" &>/dev/null ; then CURL_OUTPUT=$($SUDO curl -sfL -m 30 -o "/tmp/merge-settings.js" https://raw.githubusercontent.com/node-red/linux-installers/master/resources/merge-settings.js 2>&1) CURL_EXIT=$? echo "$CURL_OUTPUT" | $SUDO tee -a /var/log/nodered-install.log >> /dev/null if [ $CURL_EXIT -eq 0 ]; then echo " " echo "Just FYI : Your settings.js file is different from the latest defaults." read -t 30 -p "Would you like me to backup your existing settings.js and try to merge it with the new version ? (y/N) ? " yn case $yn in [Yy]* ) node /tmp/merge-settings.js --diff "$file" "/usr/lib/node_modules/node-red/settings.js" ;; * ) echo -ne "\r\n\nYou may wish to run" echo " diff -y -w --suppress-common-lines /usr/lib/node_modules/node-red/settings.js $file" echo "to compare them and see what the latest options are." ;; esac else echo " " echo "Just FYI : Your settings.js file is different from the latest defaults." echo "You may wish to run" echo " diff -y -w --suppress-common-lines /usr/lib/node_modules/node-red/settings.js $file" echo "to compare them and see what the latest options are." fi echo " " fi echo "**********************************************************************************" echo " ### WARNING ###" echo " DO NOT EXPOSE NODE-RED TO THE OPEN INTERNET WITHOUT SECURING IT FIRST" echo " " echo " Even if your Node-RED doesn't have anything valuable, (automated) attacks will" echo " happen and could provide a foothold in your local network" echo " " echo " Follow the guide at https://nodered.org/docs/user-guide/runtime/securing-node-red" echo " to setup security." echo " " echo " ### ADDITIONAL RECOMMENDATIONS ###" if [ -f /etc/sudoers.d/010_pi-nopasswd ]; then echo " - Remove the /etc/sudoers.d/010_pi-nopasswd file to require entering your password" echo " when performing any sudo/root commands:" echo " " echo " sudo rm -f /etc/sudoers.d/010_pi-nopasswd" echo " " fi if [ ! -f "$file" ]; then echo " - You can customise the initial settings by running:" echo " " echo " node-red admin init" echo " " fi if [ "$EUID" == "0" ]; then echo " - Do not run Node-RED as root or an administrative account" echo " " fi echo "**********************************************************************************" echo " " if [ ! -f "$file" ]; then initset="${INITSET}" case $initset in [Yy]* ) export HOSTIP=$(hostname -I | cut -d ' ' -f 1) $SUDO chown -Rf "$NODERED_USER:$NODERED_GROUP" "$NODERED_HOME/.node-red" 2>&1 >>/dev/null $SUDOU /usr/bin/node-red admin init ;; [Nn]* ) echo "Settings not initialized." exit 0 ;; * ) $SUDO chown -Rf "$NODERED_USER:$NODERED_GROUP" "$NODERED_HOME/.node-red/" 2>&1 >>/dev/null $SUDOU /usr/bin/node-red admin init ;; esac fi fi exit 0 ;; * ) echo " " exit 1 ;; esac else echo " " echo "Sorry - cannot connect to internet - not going to touch anything." echo "https://www.npmjs.com/package/node-red is not reachable." echo "Please ensure you have a working internet connection." echo "Return code from curl is $CURL_RC" echo " " exit 1 fi