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: