Repository navigation
Writing a Plugin
A plugin is a Python module with a Plugin class that talks to the database.
Because everything goes through the database, a new plugin needs to know nothing
about any other plugin — read the keys you need, write the keys you produce.
Start from src/fixgw/plugins/skel.py
and read doc/plugin.rst.
import threading, time
import fixgw.plugin as plugin
class MainThread(threading.Thread):
def __init__(self, parent):
super().__init__()
self.getout = False
self.parent = parent
self.log = parent.log
def run(self):
while not self.getout:
time.sleep(1)
# ...read a device, then write to the database:
# self.parent.db_write("IAS", value)
self.parent.running = False
def stop(self):
self.getout = True
class Plugin(plugin.PluginBase):
def __init__(self, name, config, config_meta):
super().__init__(name, config, config_meta)
self.thread = MainThread(self)
def run(self):
self.thread.start() # MUST return promptly
def stop(self):
self.thread.stop()
if self.thread.is_alive():
self.thread.join(1.0)
if self.thread.is_alive():
raise plugin.PluginFail # couldn't stop cleanlyRules that matter:
-
run()must return quickly. The server blocks on it during startup — put continuous work on a thread and start it here. (A pure callback plugin needs no thread at all: register callbacks inrun()and return.) -
stop()must stop cleanly and promptly. Use short I/O timeouts + a flag. Raiseplugin.PluginFailif a thread won't join, so the server can exit and be restarted rather than hang. - Set
self.running = Falsewhen your work loop ends. The server checks it about every four seconds and exits when no plugin is running. - If you override
__init__, callsuper().__init__(...)first.
| Call | Does |
|---|---|
self.db_read(key) |
read the current value |
self.db_write(key, value) |
write a value (a source's main job) |
self.db_get_item(key) |
get the raw item (to set .bad/.fail/aux directly) |
self.db_callback_add(key, fn, udata=None) |
subscribe: fn is called when key changes |
self.db_callback_del(key, fn, udata=None) |
unsubscribe (do this in stop()) |
self.config |
your plugin's settings dict (from its connection YAML) |
self.log |
a logger (.info/.debug/.warning/.error) |
Callback signature — fn(key, value, udata) — where value is the item
tuple (value, annunciate, old, bad, fail, secfail):
def on_ias(key, value, udata):
if not isinstance(value, tuple):
return # meta/aux updates arrive as non-tuples; skip
v, annunc, old, bad, fail, secfail = value
...A source reads a device and writes keys — and flags them honestly. Prefer
letting tol handle staleness (just stop writing when the device is quiet), and
set bad/fail when you know the data is wrong:
self.db_write("OILP1", psi)
self.db_get_item("OILP1").fail = sensor_open_circuitA consumer / derived plugin subscribes and reacts. Register in run(),
unregister in stop():
def run(self):
for key in self.config.get("keys", []):
self.db_callback_add(key, self.on_change)Two house patterns you'll see throughout the codebase, worth following:
- Construct-never-raises: a missing device / bad config should degrade gracefully (log + no-op), never crash the gateway on startup.
-
Ignore non-tuple callbacks: metadata/aux updates fire your callback with a
non-tuple
value— guard withisinstance(value, tuple).
Optionally return a dict of status the user can see (connection counts, message
counters). An OrderedDict keeps your fields in order:
def get_status(self):
return OrderedDict([("connected", self.connected),
("messages", self.msg_count)])Four coordinated edits load your plugin (same as any other — see Configuration):
-
connections/<name>.yaml—{ load: NAME, module: fixgw.plugins.<name>, ... } -
preferences.yaml.custom—includes: { NAME_CONFIG: connections/<name>.yaml } -
preferences.yaml.custom—enabled: { NAME: true } -
default.yaml— add- NAME_CONFIGto theconnections: include:list
Put the module under src/fixgw/plugins/, add a test under tests/, and run
make test. The suite includes tests/test_one_writer.py, which starts the
gateway from each shipped configuration and fails if any key has two writers.
- Home
- Architecture — how it works
Guides
Navigation
Plugins
Links
- Repository
- README
- Reference docs (
doc/) - pyEfis — the EFIS display
- MakerPlane