diff --git a/platypush/backend/http/static/css/source/webpanel/plugins/gpio/index.scss b/platypush/backend/http/static/css/source/webpanel/plugins/gpio/index.scss new file mode 100644 index 0000000000..8a6d3c3831 --- /dev/null +++ b/platypush/backend/http/static/css/source/webpanel/plugins/gpio/index.scss @@ -0,0 +1,39 @@ +@import 'common/vars'; + +.gpio-container { + display: flex; + flex-direction: column; + height: 100%; + overflow: auto; + + .refresh { + border-bottom: $default-border-2; + background: $default-bg-5; + + button { + border: 0; + &:hover { + color: $default-hover-fg; + } + } + } + + .pins { + .pin { + padding: 1em; + border-bottom: $default-border-2; + display: flex; + flex-direction: row; + align-items: center; + + &:nth-child(even) { + background: $modal-header-bg; + } + + &:hover { + background: $hover-bg; + } + } + } +} + diff --git a/platypush/backend/http/static/js/plugins/gpio/index.js b/platypush/backend/http/static/js/plugins/gpio/index.js new file mode 100644 index 0000000000..72d5da841d --- /dev/null +++ b/platypush/backend/http/static/js/plugins/gpio/index.js @@ -0,0 +1,35 @@ +Vue.component('gpio', { + template: '#tmpl-gpio', + props: ['config'], + + data: function() { + return { + pins: {}, + }; + }, + + methods: { + refresh: async function() { + const pins = await request('gpio.read_all'); + this.pins = pins.reduce((pins, pin) => { + pins[pin.pin] = { + name: pin.name, + number: pin.pin, + on: !!pin.value, + }; + + return pins; + }, {}); + }, + + toggle: async function(pin) { + await request('gpio.write', {pin: pin, value: +(!this.pins[pin].on)}); + this.refresh(); + }, + }, + + mounted: function() { + this.refresh(); + }, +}); + diff --git a/platypush/backend/http/templates/nav.html b/platypush/backend/http/templates/nav.html index 390474233e..9c10552752 100644 --- a/platypush/backend/http/templates/nav.html +++ b/platypush/backend/http/templates/nav.html @@ -5,6 +5,8 @@ 'camera.pi': 'fab fa-raspberry-pi', 'camera.ir.mlx90640': 'fas fa-sun', 'execute': 'fas fa-play', + 'gpio': 'fas fa-plug', + 'gpio.zeroborg': 'fas fa-robot', 'light.hue': 'fa fa-lightbulb', 'media.mplayer': 'fa fa-film', 'media.mpv': 'fa fa-film', diff --git a/platypush/backend/http/templates/plugins/gpio/index.html b/platypush/backend/http/templates/plugins/gpio/index.html new file mode 100644 index 0000000000..457f028c1f --- /dev/null +++ b/platypush/backend/http/templates/plugins/gpio/index.html @@ -0,0 +1,21 @@ + + diff --git a/platypush/plugins/gpio/__init__.py b/platypush/plugins/gpio/__init__.py index 23d803f887..6671d2c197 100644 --- a/platypush/plugins/gpio/__init__.py +++ b/platypush/plugins/gpio/__init__.py @@ -2,6 +2,7 @@ .. moduleauthor:: Fabio Manganiello """ +import threading from typing import Any, Optional, Dict, Union from platypush.plugins import Plugin, action @@ -34,10 +35,23 @@ class GpioPlugin(Plugin): super().__init__(**kwargs) self.mode = self._get_mode(mode) + self._initialized = False + self._init_lock = threading.RLock() + self._initialized_pins = {} self.pins_by_name = pins if pins else {} self.pins_by_number = {number: name for (name, number) in self.pins_by_name.items()} + def _init_board(self): + import RPi.GPIO as gpio + + with self._init_lock: + if self._initialized: + return + + gpio.setmode(self.mode) + self._initialized = True + def _get_pin_number(self, pin): try: pin = int(str(pin)) @@ -58,15 +72,13 @@ class GpioPlugin(Plugin): @action def write(self, pin: Union[int, str], value: Union[int, bool], - name: Optional[str] = None, mode: Optional[str] = None) -> Dict[str, Any]: + name: Optional[str] = None) -> Dict[str, Any]: """ Write a byte value to a pin. :param pin: PIN number or configured name :param name: Optional name for the written value (e.g. "temperature" or "humidity") :param value: Value to write - :param mode: If a PIN number is specified then you can override the default 'mode' - default parameter Response:: @@ -80,10 +92,14 @@ class GpioPlugin(Plugin): import RPi.GPIO as gpio + self._init_board() name = name or pin pin = self._get_pin_number(pin) - mode = self._get_mode(mode) if mode else self.mode - gpio.setmode(mode) + + if pin not in self._initialized_pins or self._initialized_pins[pin] != gpio.OUT: + gpio.setup(pin, gpio.OUT) + self._initialized_pins[pin] = gpio.OUT + gpio.setup(pin, gpio.OUT) gpio.output(pin, value) @@ -95,15 +111,12 @@ class GpioPlugin(Plugin): } @action - def read(self, pin: Union[int, str], name: Optional[str] = None, - mode: Optional[str] = None) -> Dict[str, Any]: + def read(self, pin: Union[int, str], name: Optional[str] = None) -> Dict[str, Any]: """ Reads a value from a PIN. :param pin: PIN number or configured name. :param name: Optional name for the read value (e.g. "temperature" or "humidity") - :param mode: If a PIN number is specified then you can override the default 'mode' - default parameter Response:: @@ -117,10 +130,14 @@ class GpioPlugin(Plugin): import RPi.GPIO as gpio + self._init_board() name = name or pin pin = self._get_pin_number(pin) - gpio.setmode(gpio.BCM) - gpio.setup(pin, gpio.IN) + + if pin not in self._initialized_pins: + gpio.setup(pin, gpio.IN) + self._initialized_pins[pin] = gpio.IN + val = gpio.input(pin) return { @@ -154,8 +171,13 @@ class GpioPlugin(Plugin): @action def cleanup(self): + """ + Cleanup the state of the GPIO and resets PIN values. + """ import RPi.GPIO as gpio gpio.cleanup() + self._initialized_pins = {} + self._initialized = False # vim:sw=4:ts=4:et: