diff --git a/node-red/Dockerfile b/node-red/Dockerfile index bc2d59a..fede821 100644 --- a/node-red/Dockerfile +++ b/node-red/Dockerfile @@ -15,14 +15,11 @@ RUN set -xe \ python3-dev \ && ln -sf /usr/bin/python3 /usr/bin/python \ && pip3 install --no-cache-dir rpi.gpio \ - six \ && npm install -g --unsafe-perm node-red \ node-red-admin \ node-red-dashboard \ - && cd /usr/lib/node_modules/node-red/nodes/core/hardware \ - && curl -sSL https://github.com/vimagick/dockerfiles/raw/master/node-red/patch/nrgpio > nrgpio \ - && curl -sSL https://github.com/vimagick/dockerfiles/raw/master/node-red/patch/nrgpio.py > nrgpio.py \ - && curl -sSL https://github.com/vimagick/dockerfiles/raw/master/node-red/patch/36-rpi-gpio.js > 36-rpi-gpio.js \ + && sed -i '1s/bash/sh/' /usr/lib/node_modules/node-red/nodes/core/hardware/nrgpio \ + && mkdir -p /usr/share/doc/python-rpi.gpio \ && apk del build-base \ python3-dev \ && rm -rf /tmp/npm-* diff --git a/node-red/arm/Dockerfile b/node-red/arm/Dockerfile index 8d58e8b..1cc53b8 100644 --- a/node-red/arm/Dockerfile +++ b/node-red/arm/Dockerfile @@ -15,14 +15,11 @@ RUN set -xe \ python3-dev \ && ln -sf /usr/bin/python3 /usr/bin/python \ && pip3 install --no-cache-dir rpi.gpio \ - six \ && npm install -g --unsafe-perm node-red \ node-red-admin \ node-red-dashboard \ - && cd /usr/lib/node_modules/node-red/nodes/core/hardware \ - && curl -sSL https://github.com/vimagick/dockerfiles/raw/master/node-red/patch/nrgpio > nrgpio \ - && curl -sSL https://github.com/vimagick/dockerfiles/raw/master/node-red/patch/nrgpio.py > nrgpio.py \ - && curl -sSL https://github.com/vimagick/dockerfiles/raw/master/node-red/patch/36-rpi-gpio.js > 36-rpi-gpio.js \ + && sed -i '1s/bash/sh/' /usr/lib/node_modules/node-red/nodes/core/hardware/nrgpio \ + && mkdir -p /usr/share/doc/python-rpi.gpio \ && apk del build-base \ python3-dev \ && rm -rf /tmp/npm-* diff --git a/node-red/patch/36-rpi-gpio.js b/node-red/patch/36-rpi-gpio.js deleted file mode 100644 index 4f7ded0..0000000 --- a/node-red/patch/36-rpi-gpio.js +++ /dev/null @@ -1,332 +0,0 @@ - -module.exports = function(RED) { - "use strict"; - var exec = require('child_process').exec; - var spawn = require('child_process').spawn; - var fs = require('fs'); - - var gpioCommand = __dirname+'/nrgpio'; - - try { - var cpuinfo = fs.readFileSync("/proc/cpuinfo").toString(); - if (cpuinfo.indexOf(": BCM") === -1) { throw "Info : "+RED._("rpi-gpio.errors.ignorenode"); } - } catch(err) { - throw "Info : "+RED._("rpi-gpio.errors.ignorenode"); - } - - try { - fs.statSync("/usr/share/doc/python-rpi.gpio"); // test on Raspbian - // /usr/lib/python3.6/dist-packages/RPi/GPIO - } catch(err) { - try { - fs.statSync("/usr/lib/python3.6/site-packages/RPi/GPIO"); // test on Arch - } - catch(err) { - try { - fs.statSync("/usr/lib/python3.6/dist-packages/RPi/GPIO"); // test on Hypriot - } - catch(err) { - RED.log.warn(RED._("rpi-gpio.errors.libnotfound")); - throw "Warning : "+RED._("rpi-gpio.errors.libnotfound"); - } - } - } - - if ( !(1 & parseInt((fs.statSync(gpioCommand).mode & parseInt("777", 8)).toString(8)[0]) )) { - RED.log.error(RED._("rpi-gpio.errors.needtobeexecutable",{command:gpioCommand})); - throw "Error : "+RED._("rpi-gpio.errors.mustbeexecutable"); - } - - // the magic to make python print stuff immediately - process.env.PYTHONUNBUFFERED = 1; - - var pinsInUse = {}; - var pinTypes = {"out":RED._("rpi-gpio.types.digout"), "tri":RED._("rpi-gpio.types.input"), "up":RED._("rpi-gpio.types.pullup"), "down":RED._("rpi-gpio.types.pulldown"), "pwm":RED._("rpi-gpio.types.pwmout")}; - - function GPIOInNode(n) { - RED.nodes.createNode(this,n); - this.buttonState = -1; - this.pin = n.pin; - this.intype = n.intype; - this.read = n.read || false; - this.debounce = Number(n.debounce || 25); - if (this.read) { this.buttonState = -2; } - var node = this; - if (!pinsInUse.hasOwnProperty(this.pin)) { - pinsInUse[this.pin] = this.intype; - } - else { - if ((pinsInUse[this.pin] !== this.intype)||(pinsInUse[this.pin] === "pwm")) { - node.warn(RED._("rpi-gpio.errors.alreadyset",{pin:this.pin,type:pinTypes[pinsInUse[this.pin]]})); - } - } - - if (node.pin !== undefined) { - node.child = spawn(gpioCommand, ["in",node.pin,node.intype,node.debounce]); - node.running = true; - node.status({fill:"green",shape:"dot",text:"common.status.ok"}); - - node.child.stdout.on('data', function (data) { - var d = data.toString().trim().split("\n"); - for (var i = 0; i < d.length; i++) { - if (d[i] === '') { return; } - if (node.running && node.buttonState !== -1 && !isNaN(Number(d[i])) && node.buttonState !== d[i]) { - node.send({ topic:"pi/"+node.pin, payload:Number(d[i]) }); - } - node.buttonState = d[i]; - node.status({fill:"green",shape:"dot",text:d[i]}); - if (RED.settings.verbose) { node.log("out: "+d[i]+" :"); } - } - }); - - node.child.stderr.on('data', function (data) { - if (RED.settings.verbose) { node.log("err: "+data+" :"); } - }); - - node.child.on('close', function (code) { - node.running = false; - node.child = null; - if (RED.settings.verbose) { node.log(RED._("rpi-gpio.status.closed")); } - if (node.done) { - node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"}); - node.done(); - } - else { node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"}); } - }); - - node.child.on('error', function (err) { - if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); } - else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); } - else { node.error(RED._("rpi-gpio.errors.error",{error:err.errno})) } - }); - - } - else { - node.warn(RED._("rpi-gpio.errors.invalidpin")+": "+node.pin); - } - - node.on("close", function(done) { - node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"}); - delete pinsInUse[node.pin]; - if (node.child != null) { - node.done = done; - node.child.stdin.write("close "+node.pin); - node.child.kill('SIGKILL'); - } - else { done(); } - }); - } - RED.nodes.registerType("rpi-gpio in",GPIOInNode); - - function GPIOOutNode(n) { - RED.nodes.createNode(this,n); - this.pin = n.pin; - this.set = n.set || false; - this.level = n.level || 0; - this.freq = n.freq || 100; - this.out = n.out || "out"; - var node = this; - if (!pinsInUse.hasOwnProperty(this.pin)) { - pinsInUse[this.pin] = this.out; - } - else { - if ((pinsInUse[this.pin] !== this.out)||(pinsInUse[this.pin] === "pwm")) { - node.warn(RED._("rpi-gpio.errors.alreadyset",{pin:this.pin,type:pinTypes[pinsInUse[this.pin]]})); - } - } - - function inputlistener(msg) { - if (msg.payload === "true") { msg.payload = true; } - if (msg.payload === "false") { msg.payload = false; } - var out = Number(msg.payload); - var limit = 1; - if (node.out === "pwm") { limit = 100; } - if ((out >= 0) && (out <= limit)) { - if (RED.settings.verbose) { node.log("out: "+out); } - if (node.child !== null) { - node.child.stdin.write(out+"\n"); - node.status({fill:"green",shape:"dot",text:msg.payload.toString()}); - } - else { - node.error(RED._("rpi-gpio.errors.pythoncommandnotfound"),msg); - node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.not-running"}); - } - } - else { node.warn(RED._("rpi-gpio.errors.invalidinput")+": "+out); } - } - - if (node.pin !== undefined) { - if (node.set && (node.out === "out")) { - node.child = spawn(gpioCommand, [node.out,node.pin,node.level]); - node.status({fill:"green",shape:"dot",text:node.level}); - } else { - node.child = spawn(gpioCommand, [node.out,node.pin,node.freq]); - node.status({fill:"green",shape:"dot",text:"common.status.ok"}); - } - node.running = true; - - node.on("input", inputlistener); - - node.child.stdout.on('data', function (data) { - if (RED.settings.verbose) { node.log("out: "+data+" :"); } - }); - - node.child.stderr.on('data', function (data) { - if (RED.settings.verbose) { node.log("err: "+data+" :"); } - }); - - node.child.on('close', function (code) { - node.child = null; - node.running = false; - if (RED.settings.verbose) { node.log(RED._("rpi-gpio.status.closed")); } - if (node.done) { - node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"}); - node.done(); - } - else { node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"}); } - }); - - node.child.on('error', function (err) { - if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); } - else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); } - else { node.error(RED._("rpi-gpio.errors.error")+': ' + err.errno); } - }); - - } - else { - node.warn(RED._("rpi-gpio.errors.invalidpin")+": "+node.pin); - } - - node.on("close", function(done) { - node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"}); - delete pinsInUse[node.pin]; - if (node.child != null) { - node.done = done; - node.child.stdin.write("close "+node.pin); - node.child.kill('SIGKILL'); - } - else { done(); } - }); - - } - RED.nodes.registerType("rpi-gpio out",GPIOOutNode); - - function PiMouseNode(n) { - RED.nodes.createNode(this,n); - this.butt = n.butt || 7; - var node = this; - - node.child = spawn(gpioCommand+".py", ["mouse",node.butt]); - node.status({fill:"green",shape:"dot",text:"common.status.ok"}); - - node.child.stdout.on('data', function (data) { - data = Number(data); - if (data === 1) { node.send({ topic:"pi/mouse", button:data, payload:1 }); } - else { node.send({ topic:"pi/mouse", button:data, payload:0 }); } - }); - - node.child.stderr.on('data', function (data) { - if (RED.settings.verbose) { node.log("err: "+data+" :"); } - }); - - node.child.on('close', function (code) { - node.child = null; - node.running = false; - if (RED.settings.verbose) { node.log(RED._("rpi-gpio.status.closed")); } - if (node.done) { - node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"}); - node.done(); - } - else { node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"}); } - }); - - node.child.on('error', function (err) { - if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); } - else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); } - else { node.error(RED._("rpi-gpio.errors.error")+': ' + err.errno); } - }); - - node.on("close", function(done) { - node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"}); - if (node.child != null) { - node.done = done; - node.child.kill('SIGINT'); - node.child = null; - } - else { done(); } - }); - } - RED.nodes.registerType("rpi-mouse",PiMouseNode); - - function PiKeyboardNode(n) { - RED.nodes.createNode(this,n); - var node = this; - - node.child = spawn(gpioCommand+".py", ["kbd","0"]); - node.status({fill:"green",shape:"dot",text:"common.status.ok"}); - - node.child.stdout.on('data', function (data) { - var b = data.toString().trim().split(","); - var act = "up"; - if (b[1] === "1") { act = "down"; } - if (b[1] === "2") { act = "repeat"; } - node.send({ topic:"pi/key", payload:Number(b[0]), action:act }); - }); - - node.child.stderr.on('data', function (data) { - if (RED.settings.verbose) { node.log("err: "+data+" :"); } - }); - - node.child.on('close', function (code) { - node.running = false; - node.child = null; - if (RED.settings.verbose) { node.log(RED._("rpi-gpio.status.closed")); } - if (node.done) { - node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"}); - node.done(); - } - else { node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"}); } - }); - - node.child.on('error', function (err) { - if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); } - else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); } - else { node.error(RED._("rpi-gpio.errors.error")+': ' + err.errno); } - }); - - node.on("close", function(done) { - node.status({}); - if (node.child != null) { - node.done = done; - node.child.kill('SIGINT'); - node.child = null; - } - else { done(); } - }); - } - RED.nodes.registerType("rpi-keyboard",PiKeyboardNode); - - var pitype = { type:"" }; - exec(gpioCommand+" info", function(err,stdout,stderr) { - if (err) { - RED.log.info(RED._("rpi-gpio.errors.version")); - } - else { - try { - var info = JSON.parse( stdout.trim().replace(/\'/g,"\"") ); - pitype.type = info["TYPE"]; - } - catch(e) { - RED.log.info(RED._("rpi-gpio.errors.sawpitype"),stdout.trim()); - } - } - }); - - RED.httpAdmin.get('/rpi-gpio/:id', RED.auth.needsPermission('rpi-gpio.read'), function(req,res) { - res.json(pitype); - }); - - RED.httpAdmin.get('/rpi-pins/:id', RED.auth.needsPermission('rpi-gpio.read'), function(req,res) { - res.json(pinsInUse); - }); -} diff --git a/node-red/patch/README.md b/node-red/patch/README.md deleted file mode 100644 index 2bc6d0f..0000000 --- a/node-red/patch/README.md +++ /dev/null @@ -1,4 +0,0 @@ -Node-RED PATCH -============== - -python2 (on alpine linux) cannot read usb keyboard input device diff --git a/node-red/patch/nrgpio b/node-red/patch/nrgpio deleted file mode 100755 index 3488030..0000000 --- a/node-red/patch/nrgpio +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -# -# Copyright JS Foundation and other contributors, http://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. -# - -BASEDIR=$(dirname $0) -python -u $BASEDIR/nrgpio.py $@ diff --git a/node-red/patch/nrgpio.py b/node-red/patch/nrgpio.py deleted file mode 100755 index 9fa5b8a..0000000 --- a/node-red/patch/nrgpio.py +++ /dev/null @@ -1,245 +0,0 @@ -#!/usr/bin/python -# -# Copyright JS Foundation and other contributors, http://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. -# - -# Import library functions we need -import six - -try: - import RPi.GPIO as GPIO -except: - class GPIO: - BOARD = None - @staticmethod - def setmode(*args): pass - @staticmethod - def setwarnings(*args): pass - -import struct -import sys -import os -import subprocess -from time import sleep - -bounce = 25; - -if len(sys.argv) > 2: - cmd = sys.argv[1].lower() - pin = int(sys.argv[2]) - GPIO.setmode(GPIO.BOARD) - GPIO.setwarnings(False) - - if cmd == "pwm": - #print "Initialised pin "+str(pin)+" to PWM" - try: - freq = int(sys.argv[3]) - except: - freq = 100 - - GPIO.setup(pin,GPIO.OUT) - p = GPIO.PWM(pin, freq) - p.start(0) - - while True: - try: - data = six.moves.input() - if 'close' in data: - sys.exit(0) - p.ChangeDutyCycle(float(data)) - except (EOFError, SystemExit): # hopefully always caused by us sigint'ing the program - GPIO.cleanup(pin) - sys.exit(0) - except Exception as ex: - six.print_("bad data: "+data) - - elif cmd == "buzz": - #print "Initialised pin "+str(pin)+" to Buzz" - GPIO.setup(pin,GPIO.OUT) - p = GPIO.PWM(pin, 100) - p.stop() - - while True: - try: - data = six.moves.input() - if 'close' in data: - sys.exit(0) - elif float(data) == 0: - p.stop() - else: - p.start(50) - p.ChangeFrequency(float(data)) - except (EOFError, SystemExit): # hopefully always caused by us sigint'ing the program - GPIO.cleanup(pin) - sys.exit(0) - except Exception as ex: - six.print_("bad data: "+data) - - elif cmd == "out": - #print "Initialised pin "+str(pin)+" to OUT" - GPIO.setup(pin,GPIO.OUT) - if len(sys.argv) == 4: - GPIO.output(pin,int(sys.argv[3])) - - while True: - try: - data = six.moves.input() - if 'close' in data: - sys.exit(0) - data = int(data) - except (EOFError, SystemExit): # hopefully always caused by us sigint'ing the program - GPIO.cleanup(pin) - sys.exit(0) - except: - if len(sys.argv) == 4: - data = int(sys.argv[3]) - else: - data = 0 - if data != 0: - data = 1 - GPIO.output(pin,data) - - elif cmd == "in": - #print "Initialised pin "+str(pin)+" to IN" - bounce = float(sys.argv[4]) - def handle_callback(chan): - sleep(bounce/1000.0) - six.print_(GPIO.input(chan)) - - if sys.argv[3].lower() == "up": - GPIO.setup(pin,GPIO.IN,GPIO.PUD_UP) - elif sys.argv[3].lower() == "down": - GPIO.setup(pin,GPIO.IN,GPIO.PUD_DOWN) - else: - GPIO.setup(pin,GPIO.IN) - - six.print_(GPIO.input(pin)) - GPIO.add_event_detect(pin, GPIO.BOTH, callback=handle_callback, bouncetime=int(bounce)) - - while True: - try: - data = six.moves.input() - if 'close' in data: - sys.exit(0) - except (EOFError, SystemExit): # hopefully always caused by us sigint'ing the program - GPIO.cleanup(pin) - sys.exit(0) - - elif cmd == "byte": - #print "Initialised BYTE mode - "+str(pin)+ - list = [7,11,13,12,15,16,18,22] - GPIO.setup(list,GPIO.OUT) - - while True: - try: - data = six.moves.input() - if 'close' in data: - sys.exit(0) - data = int(data) - except (EOFError, SystemExit): # hopefully always caused by us sigint'ing the program - GPIO.cleanup() - sys.exit(0) - except: - data = 0 - for bit in range(8): - if pin == 1: - mask = 1 << (7 - bit) - else: - mask = 1 << bit - GPIO.output(list[bit], data & mask) - - elif cmd == "borg": - #print "Initialised BORG mode - "+str(pin)+ - GPIO.setup(11,GPIO.OUT) - GPIO.setup(13,GPIO.OUT) - GPIO.setup(15,GPIO.OUT) - r = GPIO.PWM(11, 100) - g = GPIO.PWM(13, 100) - b = GPIO.PWM(15, 100) - r.start(0) - g.start(0) - b.start(0) - - while True: - try: - data = six.moves.input() - if 'close' in data: - sys.exit(0) - c = data.split(",") - r.ChangeDutyCycle(float(c[0])) - g.ChangeDutyCycle(float(c[1])) - b.ChangeDutyCycle(float(c[2])) - except (EOFError, SystemExit): # hopefully always caused by us sigint'ing the program - GPIO.cleanup() - sys.exit(0) - except: - data = 0 - - elif cmd == "mouse": # catch mice button events - file = open( "/dev/input/mice", "rb" ) - oldbutt = 0 - - def getMouseEvent(): - global oldbutt - global pin - buf = file.read(3) - pin = pin & 0x07 - button = six.byte2int(buf) & pin # mask out just the required button(s) - if button != oldbutt: # only send if changed - oldbutt = button - six.print_(button) - - while True: - try: - getMouseEvent() - except: - file.close() - sys.exit(0) - - elif cmd == "kbd": # catch keyboard button events - try: - while not os.path.isdir("/dev/input/by-path"): - time.sleep(10) - infile = subprocess.check_output("ls /dev/input/by-path/ | grep -m 1 'kbd'", shell=True).strip() - infile_path = "/dev/input/by-path/" + infile.decode('utf-8') - EVENT_SIZE = struct.calcsize('llHHI') - file = open(infile_path, "rb") - event = file.read(EVENT_SIZE) - while event: - (tv_sec, tv_usec, type, code, value) = struct.unpack('llHHI', event) - #if type != 0 or code != 0 or value != 0: - if type == 1: - # type,code,value - six.print_("%u,%u" % (code, value)) - event = file.read(EVENT_SIZE) - six.print_("0,0") - file.close() - sys.exit(0) - except: - file.close() - sys.exit(0) - -elif len(sys.argv) > 1: - cmd = sys.argv[1].lower() - if cmd == "rev": - six.print_(GPIO.RPI_REVISION) - elif cmd == "ver": - six.print_(GPIO.VERSION) - elif cmd == "info": - six.print_(GPIO.RPI_INFO) - else: - six.print_("Bad parameters - in|out|pwm|buzz|byte|borg|mouse|kbd|ver|info {pin} {value|up|down}") - six.print_(" only ver (gpio version) and info (board information) accept no pin parameter.") - -else: - six.print_("Bad parameters - in|out|pwm|buzz|byte|borg|mouse|kbd|ver|info {pin} {value|up|down}") diff --git a/node-red/patch/screenshot.png b/node-red/patch/screenshot.png deleted file mode 100644 index 04a19b7..0000000 Binary files a/node-red/patch/screenshot.png and /dev/null differ