initial commit

This commit is contained in:
Evert Prants 2020-09-18 17:19:44 +03:00
commit bc0cef6fe3
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
12 changed files with 413 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/bin/
/.settings/
/.classpath
/.project

4
README.md Normal file
View File

@ -0,0 +1,4 @@
# RedstoneOutput
Custom Spigot plugin for interacting with Raspberry Pi GPIO outputs.
This plugin uses a client-server model for interacting with GPIO because the pi is too slow for Spigot. Will try native when I get my RasPi 4 8GB. `gpio-server.py` is to be run on the pi.

114
gpio-server.py Normal file
View File

@ -0,0 +1,114 @@
#!/usr/bin/env python3
# Pins controllable by client
# Types:
# 1: switch on or off depending on power
# 2: toggle on or off with single power high event
# 3: addressable (TODO)
# 4: on/off event to client (TODO)
# 5: analog event to client (TODO)
import socket, threading
import RPi.GPIO as GPIO
import configparser
import os, io
# Initialize GPIO
# Pinout help: https://pinout.xyz/
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
configfile = "server.ini"
config = configparser.ConfigParser()
# Check if there is already a configurtion file
if not os.path.isfile(configfile):
# Create the configuration file as it doesn't exist yet
cfgfile = open(configfile, "w")
# Add content to the file
config.add_section("server")
config.set("server", "host", "0.0.0.0")
config.set("server", "port", "62002")
config.add_section("output")
config.set("output", "type", "1")
config.set("output", "pin", "8")
config.write(cfgfile)
cfgfile.close()
else:
# Load config from file
config = configparser.ConfigParser()
config.read(configfile)
# Set up all pins
for section in config.sections():
if section == "server":
continue
type = int(config.get(section, "type"))
pin = int(config.get(section, "pin"))
if type == 1:
GPIO.setup(pin, GPIO.OUT, initial=GPIO.LOW)
# Client connection thread
class ClientThread(threading.Thread):
def __init__(self, ip, port, clientsocket):
threading.Thread.__init__(self)
self.ip = ip
self.port = port
self.csocket = clientsocket
print("[+] New thread started for %s:%s" % (ip, str(port)))
def handler(self, power, key, val):
if not config.has_section(key):
return
type = int(config.get(key, "type"))
pin = int(config.get(key, "pin"))
if type == 1:
if power > 0:
GPIO.output(pin, GPIO.HIGH)
else:
GPIO.output(pin, GPIO.LOW)
def srvcmd(self,line):
split = line[:-2].split(':')
if split[0] and split[1]:
self.handler(int(split[0]), split[1], split[2])
def run(self):
self.csocket.send(b'connected\r\n')
# Receive messages from client
while True:
data = self.csocket.recv(1024)
if not data:
break
data = data.decode('utf-8')
# Handle command from client
self.srvcmd(data)
self.csocket.close()
print("Client at %s disconnected..." % (self.ip))
def run_sockets():
HOST = config.get("server", "host")
PORT = int(config.get("server", "port"))
# Start server socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST,PORT))
print("Bound to %s:%s" % (HOST,PORT))
while True:
s.listen(4)
print("Listening for incoming connections...")
(clientsock, (ip, port)) = s.accept()
# New thread for new client
newthread = ClientThread(ip, port, clientsock)
newthread.start()
run_sockets()

8
plugin.yml Normal file
View File

@ -0,0 +1,8 @@
name: RedstoneOutput
main: ee.lunasqu.redstoneoutput.Main
version: 1.0
api-version: 1.13
commands:
rspi:
description: Control the remote connection for RedstoneOutput
usage: /<command> start|stop|status

View File

@ -0,0 +1,90 @@
package ee.lunasqu.redstoneoutput;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitRunnable;
import ee.lunasqu.redstoneoutput.events.ConnectionEstablishedEvent;
import ee.lunasqu.redstoneoutput.events.DisconnectedEvent;
public class Connection extends BukkitRunnable {
private Socket socket;
private BufferedWriter writer;
private BufferedReader reader;
private boolean alive;
public Connection(String addr, int port) {
try {
socket = new Socket(addr, port);
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
Bukkit.getPluginManager().callEvent(new DisconnectedEvent());
return;
}
// Successful connection
Bukkit.getPluginManager().callEvent(new ConnectionEstablishedEvent());
alive = true;
}
public boolean sendStateChange (int power, String key, String value) {
if (!alive) return false;
try {
writer.write(power + ":" + key + ":" + value + "\r\n");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
public void die () {
this.alive = false;
if (!socket.isClosed()) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public boolean isAlive() {
return this.alive;
}
@Override
public void run() {
if (!this.alive) {
if (!this.isCancelled()) this.cancel();
return;
}
try {
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (!alive) break;
}
alive = false;
if (!socket.isClosed()) {
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
Bukkit.getPluginManager().callEvent(new DisconnectedEvent());
this.cancel();
}
}

View File

@ -0,0 +1,70 @@
package ee.lunasqu.redstoneoutput;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockRedstoneEvent;
import ee.lunasqu.redstoneoutput.events.ConnectionEstablishedEvent;
import ee.lunasqu.redstoneoutput.events.DisconnectedEvent;
public class EventListener implements Listener {
private static Material[] SIGNS = {
Material.ACACIA_SIGN, Material.ACACIA_WALL_SIGN,
Material.BIRCH_SIGN, Material.BIRCH_WALL_SIGN,
Material.CRIMSON_SIGN, Material.CRIMSON_WALL_SIGN,
Material.DARK_OAK_SIGN, Material.DARK_OAK_WALL_SIGN,
Material.JUNGLE_SIGN, Material.JUNGLE_WALL_SIGN,
Material.OAK_SIGN, Material.OAK_WALL_SIGN,
Material.SPRUCE_SIGN, Material.SPRUCE_WALL_SIGN
};
@EventHandler
public void onRedstone(BlockRedstoneEvent e) {
Block b = e.getBlock();
Location[] sides = {
b.getLocation().add(new Location(b.getWorld(), 0, 1.0, 0)),
b.getLocation().add(new Location(b.getWorld(), -1.0, 0, 0)),
b.getLocation().add(new Location(b.getWorld(), 1.0, 0, 0)),
b.getLocation().add(new Location(b.getWorld(), 0, 0, 1.0)),
b.getLocation().add(new Location(b.getWorld(), 0, 0, -1.0))
};
Sign sign = null;
List<Material> checklist = Arrays.asList(EventListener.SIGNS);
for (Location p : sides) {
Block test = b.getWorld().getBlockAt(p);
if (checklist.contains(test.getType())) {
Sign i = (Sign) test.getState();
String l = i.getLine(0);
if (l.equalsIgnoreCase("[Output]")) {
sign = i;
break;
}
}
}
if (sign == null) return;
String key = sign.getLine(1);
String value = sign.getLine(2);
Connection t = Main.plugin.getConnection();
if (t == null) return;
t.sendStateChange(e.getNewCurrent(), key, value);
}
@EventHandler
public void onConnClose(DisconnectedEvent e) {
Main.plugin.getLogger().info("Connection to remote failed.");
}
@EventHandler
public void onConnEstab(ConnectionEstablishedEvent e) {
Main.plugin.getLogger().info("Connection to remote established!");
}
}

View File

@ -0,0 +1,40 @@
package ee.lunasqu.redstoneoutput;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
public class Main extends JavaPlugin {
private FileConfiguration config;
public static Main plugin;
public Connection service;
public Connection getConnection() {
if (service == null || !service.isAlive()) return null;
return service;
}
@Override
public void onEnable() {
config = this.getConfig();
config.addDefault("host", "127.0.0.1");
config.addDefault("port", 62002);
config.options().copyDefaults(true);
this.saveConfig();
plugin = this;
getServer().getPluginManager().registerEvents(new EventListener(), this);
this.getCommand("rspi").setExecutor(new RemoteCommand());
this.connect();
}
@Override
public void onDisable() {
if (service != null && service.isAlive()) service.die();
}
public void connect () {
service = new Connection(this.config.getString("host"), this.config.getInt("port"));
service.runTaskAsynchronously(this);
}
}

View File

@ -0,0 +1,37 @@
package ee.lunasqu.redstoneoutput;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class RemoteCommand implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command arg1, String arg2, String[] arguments) {
switch (arguments[0]) {
case "status":
if (Main.plugin.getConnection() != null) {
sender.sendMessage("The connection is established.");
} else {
sender.sendMessage("The connection is closed.");
}
return true;
case "start":
if (Main.plugin.getConnection() != null) {
sender.sendMessage("The connection is already established!");
return false;
}
sender.sendMessage("Connection has been attempted!");
Main.plugin.connect();
return true;
case "stop":
if (Main.plugin.getConnection() == null) {
sender.sendMessage("The connection is already closed!");
return false;
}
sender.sendMessage("Connection has been closed!");
Main.plugin.getConnection().die();
return true;
}
return false;
}
}

View File

@ -0,0 +1,21 @@
package ee.lunasqu.redstoneoutput.events;
import org.bukkit.Bukkit;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class BaseEvent extends Event {
private static final HandlerList HANDLERS = new HandlerList();
public BaseEvent () {
super(!Bukkit.getServer().isPrimaryThread());
}
public HandlerList getHandlers() {
return HANDLERS;
}
public static HandlerList getHandlerList() {
return HANDLERS;
}
}

View File

@ -0,0 +1,5 @@
package ee.lunasqu.redstoneoutput.events;
public class ConnectionEstablishedEvent extends BaseEvent {
}

View File

@ -0,0 +1,5 @@
package ee.lunasqu.redstoneoutput.events;
public class DisconnectedEvent extends BaseEvent {
}

View File

@ -0,0 +1,15 @@
package ee.lunasqu.redstoneoutput.events;
public class InputEvent extends BaseEvent {
private String key;
private String value;
public InputEvent (String key, String value) {
super();
this.key = key;
this.value = value;
}
public String getKey () { return this.key; }
public String getValue () { return this.value; }
}