2020-07-27 21:08:48 +03:00
|
|
|
# Raspberry Pi Power Control
|
|
|
|
#
|
|
|
|
# Copyright (C) 2020 Jordan Ruthe <jordanruthe@gmail.com>
|
|
|
|
#
|
|
|
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
|
|
|
|
|
|
|
import logging
|
|
|
|
import os
|
2020-07-30 01:18:54 +03:00
|
|
|
from tornado.ioloop import IOLoop
|
|
|
|
from tornado import gen
|
2020-07-27 21:08:48 +03:00
|
|
|
|
|
|
|
class PrinterPower:
|
2020-08-06 04:06:52 +03:00
|
|
|
def __init__(self, config):
|
|
|
|
self.server = config.get_server()
|
2020-07-27 21:08:48 +03:00
|
|
|
self.server.register_endpoint(
|
2020-09-03 14:28:42 +03:00
|
|
|
"/machine/gpio_power/devices", ['GET'],
|
2020-07-27 21:08:48 +03:00
|
|
|
self._handle_list_devices)
|
|
|
|
self.server.register_endpoint(
|
2020-09-03 14:28:42 +03:00
|
|
|
"/machine/gpio_power/status", ['GET'],
|
2020-07-27 21:08:48 +03:00
|
|
|
self._handle_power_request)
|
|
|
|
self.server.register_endpoint(
|
2020-09-03 14:28:42 +03:00
|
|
|
"/machine/gpio_power/on", ['POST'],
|
2020-07-27 21:08:48 +03:00
|
|
|
self._handle_power_request)
|
|
|
|
self.server.register_endpoint(
|
2020-09-03 14:28:42 +03:00
|
|
|
"/machine/gpio_power/off", ['POST'],
|
2020-07-27 21:08:48 +03:00
|
|
|
self._handle_power_request)
|
|
|
|
|
|
|
|
self.current_dev = None
|
|
|
|
self.devices = {}
|
2020-08-06 04:06:52 +03:00
|
|
|
dev_names = config.get('devices')
|
|
|
|
dev_names = [d.strip() for d in dev_names.split(',') if d.strip()]
|
|
|
|
logging.info("Power plugin loading devices: " + str(dev_names))
|
|
|
|
devices = {}
|
|
|
|
for dev in dev_names:
|
|
|
|
pin = config.getint(dev + "_pin")
|
|
|
|
name = config.get(dev + "_name", dev)
|
|
|
|
active_low = config.getboolean(dev + "_active_low", False)
|
|
|
|
devices[dev] = {
|
|
|
|
"name": name,
|
|
|
|
"pin": pin,
|
|
|
|
"active_low": int(active_low),
|
|
|
|
"status": None
|
|
|
|
}
|
|
|
|
ioloop = IOLoop.current()
|
|
|
|
ioloop.spawn_callback(self.initialize_devices, devices)
|
2020-07-27 21:08:48 +03:00
|
|
|
|
|
|
|
async def _handle_list_devices(self, path, method, args):
|
|
|
|
output = {"devices": []}
|
|
|
|
for dev in self.devices:
|
|
|
|
output['devices'].append({
|
|
|
|
"name": self.devices[dev]["name"],
|
|
|
|
"id": dev
|
|
|
|
})
|
|
|
|
return output
|
|
|
|
|
|
|
|
async def _handle_power_request(self, path, method, args):
|
|
|
|
if len(args) == 0:
|
2020-09-05 19:36:17 +03:00
|
|
|
if path == "/machine/gpio_power/status":
|
2020-07-27 21:08:48 +03:00
|
|
|
args = self.devices
|
|
|
|
else:
|
|
|
|
return "no_devices"
|
|
|
|
|
|
|
|
result = {}
|
|
|
|
for dev in args:
|
|
|
|
if dev not in self.devices:
|
|
|
|
result[dev] = "device_not_found"
|
|
|
|
continue
|
|
|
|
|
2020-07-30 01:18:54 +03:00
|
|
|
await GPIO.verify_pin(self.devices[dev]["pin"],
|
|
|
|
self.devices[dev]["active_low"])
|
2020-09-05 19:36:17 +03:00
|
|
|
if path == "/machine/gpio_power/on":
|
2020-07-27 21:08:48 +03:00
|
|
|
GPIO.set_pin_value(self.devices[dev]["pin"], 1)
|
2020-09-05 19:36:17 +03:00
|
|
|
elif path == "/machine/gpio_power/off":
|
2020-07-27 21:08:48 +03:00
|
|
|
GPIO.set_pin_value(self.devices[dev]["pin"], 0)
|
2020-09-05 19:36:17 +03:00
|
|
|
elif path != "/machine/gpio_power/status":
|
2020-07-27 21:08:48 +03:00
|
|
|
raise self.server.error("Unsupported power request")
|
|
|
|
|
2020-07-30 00:46:27 +03:00
|
|
|
self.devices[dev]["status"] = GPIO.is_pin_on(
|
|
|
|
self.devices[dev]["pin"])
|
2020-07-27 21:08:48 +03:00
|
|
|
|
|
|
|
result[dev] = self.devices[dev]["status"]
|
|
|
|
return result
|
|
|
|
|
2020-07-30 01:18:54 +03:00
|
|
|
async def initialize_devices(self, devices):
|
|
|
|
for name, device in devices.items():
|
2020-07-27 21:08:48 +03:00
|
|
|
try:
|
2020-07-30 00:46:27 +03:00
|
|
|
logging.debug(
|
2020-08-11 19:59:47 +03:00
|
|
|
f"Attempting to configure pin GPIO{device['pin']}")
|
2020-07-30 01:18:54 +03:00
|
|
|
await GPIO.setup_pin(device["pin"], device["active_low"])
|
|
|
|
device["status"] = GPIO.is_pin_on(device["pin"])
|
2020-07-30 00:46:27 +03:00
|
|
|
except Exception:
|
2020-07-30 01:18:54 +03:00
|
|
|
logging.exception(
|
2020-08-11 19:59:47 +03:00
|
|
|
f"Power plugin: ERR Problem configuring the output pin for"
|
|
|
|
f" device {name}. Removing device")
|
2020-07-27 21:08:48 +03:00
|
|
|
continue
|
2020-07-30 01:18:54 +03:00
|
|
|
self.devices[name] = device
|
2020-07-27 21:08:48 +03:00
|
|
|
|
|
|
|
class GPIO:
|
|
|
|
gpio_root = "/sys/class/gpio"
|
|
|
|
|
|
|
|
@staticmethod
|
2020-08-11 19:59:47 +03:00
|
|
|
def _set_gpio_option(pin, option, value):
|
2020-07-27 21:08:48 +03:00
|
|
|
GPIO._write(
|
2020-08-11 19:59:47 +03:00
|
|
|
os.path.join(GPIO.gpio_root, f"gpio{pin}", option),
|
2020-07-27 21:08:48 +03:00
|
|
|
value
|
|
|
|
)
|
|
|
|
|
2020-07-30 00:46:27 +03:00
|
|
|
@staticmethod
|
2020-07-27 21:08:48 +03:00
|
|
|
def _get_gpio_option(pin, option):
|
|
|
|
return GPIO._read(
|
2020-08-11 19:59:47 +03:00
|
|
|
os.path.join(GPIO.gpio_root, f"gpio{pin}", option)
|
2020-07-27 21:08:48 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def _write(file, data):
|
|
|
|
with open(file, 'w') as f:
|
|
|
|
f.write(str(data))
|
|
|
|
f.flush()
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def _read(file):
|
|
|
|
with open(file, 'r') as f:
|
|
|
|
f.seek(0)
|
|
|
|
return f.read().strip()
|
|
|
|
|
|
|
|
@staticmethod
|
2020-07-30 01:18:54 +03:00
|
|
|
async def verify_pin(pin, active_low=1):
|
2020-08-11 19:59:47 +03:00
|
|
|
gpiopath = os.path.join(GPIO.gpio_root, f"gpio{pin}")
|
2020-07-27 21:08:48 +03:00
|
|
|
if not os.path.exists(gpiopath):
|
2020-08-11 19:59:47 +03:00
|
|
|
logging.info(f"Re-intializing GPIO{pin}")
|
2020-07-30 01:18:54 +03:00
|
|
|
await GPIO.setup_pin(pin, active_low)
|
2020-07-27 21:08:48 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
if GPIO._get_gpio_option(pin, "active_low").strip() != str(active_low):
|
|
|
|
GPIO._set_gpio_option(pin, "active_low", active_low)
|
|
|
|
|
|
|
|
if GPIO._get_gpio_option(pin, "direction").strip() != "out":
|
|
|
|
GPIO._set_gpio_option(pin, "direction", "out")
|
|
|
|
|
|
|
|
@staticmethod
|
2020-07-30 01:18:54 +03:00
|
|
|
async def setup_pin(pin, active_low=1):
|
2020-07-27 21:08:48 +03:00
|
|
|
pin = int(pin)
|
|
|
|
active_low = 1 if active_low == 1 else 0
|
|
|
|
|
2020-08-11 19:59:47 +03:00
|
|
|
gpiopath = os.path.join(GPIO.gpio_root, f"gpio{pin}")
|
2020-07-27 21:08:48 +03:00
|
|
|
if not os.path.exists(gpiopath):
|
|
|
|
GPIO._write(
|
|
|
|
os.path.join(GPIO.gpio_root, "export"),
|
|
|
|
pin)
|
2020-08-11 19:59:47 +03:00
|
|
|
logging.info(f"Waiting for GPIO{pin} to initialize")
|
2020-07-30 00:46:27 +03:00
|
|
|
while os.stat(os.path.join(
|
2020-08-11 19:59:47 +03:00
|
|
|
GPIO.gpio_root, f"gpio{pin}",
|
2020-07-30 01:18:54 +03:00
|
|
|
"active_low")).st_gid == 0:
|
|
|
|
await gen.sleep(.1)
|
2020-07-27 21:08:48 +03:00
|
|
|
|
|
|
|
if GPIO._get_gpio_option(pin, "active_low").strip() != str(active_low):
|
|
|
|
GPIO._set_gpio_option(pin, "active_low", active_low)
|
2020-07-30 00:46:27 +03:00
|
|
|
|
2020-07-27 21:08:48 +03:00
|
|
|
if GPIO._get_gpio_option(pin, "direction").strip() != "out":
|
|
|
|
GPIO._set_gpio_option(pin, "direction", "out")
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def is_pin_on(pin):
|
|
|
|
return "on" if int(GPIO._get_gpio_option(pin, "value")) else "off"
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def set_pin_value(pin, active):
|
|
|
|
value = 1 if (active == 1) else 0
|
|
|
|
GPIO._set_gpio_option(pin, "value", value)
|
|
|
|
|
|
|
|
|
2020-08-06 04:06:52 +03:00
|
|
|
def load_plugin(config):
|
|
|
|
return PrinterPower(config)
|