2022-02-26 21:45:10 +01:00
|
|
|
|
|
|
|
import importlib
|
2018-11-04 00:47:26 +01:00
|
|
|
import logging
|
|
|
|
import os
|
2022-02-26 21:45:10 +01:00
|
|
|
import pkgutil
|
|
|
|
|
2018-11-04 00:47:26 +01:00
|
|
|
from importlib import import_module
|
2022-02-26 21:45:10 +01:00
|
|
|
|
2019-07-31 07:58:54 +02:00
|
|
|
from cbpi.api import *
|
|
|
|
from cbpi.utils.utils import load_config
|
2022-02-26 21:45:10 +01:00
|
|
|
from importlib_metadata import metadata, version
|
2018-12-29 00:27:19 +01:00
|
|
|
|
2019-07-31 07:58:54 +02:00
|
|
|
logger = logging.getLogger(__name__)
|
2018-11-04 00:47:26 +01:00
|
|
|
|
|
|
|
class PluginController():
|
|
|
|
modules = {}
|
2018-11-16 20:35:59 +01:00
|
|
|
types = {}
|
2018-11-04 00:47:26 +01:00
|
|
|
|
|
|
|
def __init__(self, cbpi):
|
|
|
|
self.cbpi = cbpi
|
2019-07-31 07:58:54 +02:00
|
|
|
|
2018-12-07 00:18:35 +01:00
|
|
|
def load_plugins(self):
|
2018-11-04 00:47:26 +01:00
|
|
|
|
2022-02-26 21:45:10 +01:00
|
|
|
this_directory = os.sep.join(
|
|
|
|
os.path.abspath(__file__).split(os.sep)[:-1])
|
2019-01-05 20:43:48 +01:00
|
|
|
for filename in os.listdir(os.path.join(this_directory, "../extension")):
|
2021-02-01 01:38:04 +01:00
|
|
|
if os.path.isdir(
|
|
|
|
os.path.join(this_directory, "../extension/") + filename) is False or filename == "__pycache__":
|
2018-11-04 00:47:26 +01:00
|
|
|
continue
|
|
|
|
try:
|
|
|
|
logger.info("Trying to load plugin %s" % filename)
|
2021-01-17 22:49:18 +01:00
|
|
|
data = load_config(os.path.join(
|
2022-02-26 21:45:10 +01:00
|
|
|
this_directory, "../extension/%s/config.yaml" % filename))
|
2019-08-05 20:51:20 +02:00
|
|
|
if (data.get("active") is True and data.get("version") == 4):
|
2021-01-17 22:49:18 +01:00
|
|
|
self.modules[filename] = import_module(
|
|
|
|
"cbpi.extension.%s" % (filename))
|
2018-11-16 20:35:59 +01:00
|
|
|
self.modules[filename].setup(self.cbpi)
|
2021-02-01 01:38:04 +01:00
|
|
|
# logger.info("Plugin %s loaded successful" % filename)
|
2018-11-04 00:47:26 +01:00
|
|
|
else:
|
2021-01-17 22:49:18 +01:00
|
|
|
logger.warning(
|
|
|
|
"Plugin %s is not supporting version 4" % filename)
|
2018-11-04 00:47:26 +01:00
|
|
|
|
|
|
|
except Exception as e:
|
2022-02-26 21:45:10 +01:00
|
|
|
|
2018-11-04 00:47:26 +01:00
|
|
|
logger.error(e)
|
|
|
|
|
2018-12-29 00:27:19 +01:00
|
|
|
def load_plugins_from_evn(self):
|
2021-02-01 01:38:04 +01:00
|
|
|
|
2022-02-26 21:45:10 +01:00
|
|
|
discovered_plugins = {
|
|
|
|
name: importlib.import_module(name)
|
|
|
|
for finder, name, ispkg
|
|
|
|
in pkgutil.iter_modules()
|
|
|
|
if name.startswith('cbpi') and len(name) > 4
|
|
|
|
}
|
2021-02-01 01:38:04 +01:00
|
|
|
|
2022-02-26 21:45:10 +01:00
|
|
|
for key, value in discovered_plugins.items():
|
|
|
|
from importlib.metadata import version
|
2018-12-31 00:22:00 +01:00
|
|
|
try:
|
2022-02-26 21:45:10 +01:00
|
|
|
logger.info("Try to load plugin: {} == {} ".format(
|
|
|
|
key, version(key)))
|
|
|
|
value.setup(self.cbpi)
|
|
|
|
logger.info("Plugin {} loaded successfully".format(key))
|
2018-12-31 00:22:00 +01:00
|
|
|
except Exception as e:
|
2022-02-26 21:45:10 +01:00
|
|
|
logger.error("FAILED to load plugin {} ".format(key))
|
2018-12-31 00:22:00 +01:00
|
|
|
logger.error(e)
|
2018-12-29 00:27:19 +01:00
|
|
|
|
2018-11-01 21:25:42 +01:00
|
|
|
def register(self, name, clazz) -> None:
|
|
|
|
'''
|
|
|
|
Register a new actor type
|
|
|
|
:param name: actor name
|
|
|
|
:param clazz: actor class
|
|
|
|
:return: None
|
|
|
|
'''
|
2021-01-22 23:25:20 +01:00
|
|
|
logger.debug("Register %s Class %s" % (name, clazz.__name__))
|
2021-02-01 01:38:04 +01:00
|
|
|
|
2021-01-23 14:41:26 +01:00
|
|
|
if issubclass(clazz, CBPiActor):
|
2021-02-01 01:38:04 +01:00
|
|
|
self.cbpi.actor.types[name] = self._parse_step_props(clazz, name)
|
2018-11-18 15:40:10 +01:00
|
|
|
|
2021-01-23 14:41:26 +01:00
|
|
|
if issubclass(clazz, CBPiKettleLogic):
|
2021-02-01 01:38:04 +01:00
|
|
|
self.cbpi.kettle.types[name] = self._parse_step_props(clazz, name)
|
|
|
|
|
2022-01-02 11:25:56 +01:00
|
|
|
if issubclass(clazz, CBPiFermenterLogic):
|
2022-02-26 21:45:10 +01:00
|
|
|
self.cbpi.fermenter.types[name] = self._parse_step_props(
|
|
|
|
clazz, name)
|
2022-01-02 11:25:56 +01:00
|
|
|
|
2021-01-23 14:41:26 +01:00
|
|
|
if issubclass(clazz, CBPiSensor):
|
2021-02-01 01:38:04 +01:00
|
|
|
self.cbpi.sensor.types[name] = self._parse_step_props(clazz, name)
|
2018-12-29 00:27:19 +01:00
|
|
|
|
2021-01-17 22:49:18 +01:00
|
|
|
if issubclass(clazz, CBPiStep):
|
2021-02-01 01:38:04 +01:00
|
|
|
self.cbpi.step.types[name] = self._parse_step_props(clazz, name)
|
2021-01-17 22:49:18 +01:00
|
|
|
|
2022-01-05 06:46:35 +01:00
|
|
|
if issubclass(clazz, CBPiFermentationStep):
|
2022-02-26 21:45:10 +01:00
|
|
|
self.cbpi.fermenter.steptypes[name] = self._parse_step_props(
|
|
|
|
clazz, name)
|
2022-01-05 06:46:35 +01:00
|
|
|
|
2018-12-06 23:46:06 +01:00
|
|
|
if issubclass(clazz, CBPiExtension):
|
2019-07-27 21:08:19 +02:00
|
|
|
self.c = clazz(self.cbpi)
|
2018-11-01 19:50:04 +01:00
|
|
|
|
2021-01-17 22:49:18 +01:00
|
|
|
def _parse_property_object(self, p):
|
|
|
|
if isinstance(p, Property.Number):
|
2021-02-01 01:38:04 +01:00
|
|
|
return {"label": p.label, "type": "number", "configurable": p.configurable, "description": p.description,
|
|
|
|
"default_value": p.default_value}
|
2021-01-17 22:49:18 +01:00
|
|
|
elif isinstance(p, Property.Text):
|
2021-02-01 01:38:04 +01:00
|
|
|
return {"label": p.label, "type": "text", "configurable": p.configurable, "default_value": p.default_value,
|
|
|
|
"description": p.description}
|
2021-01-17 22:49:18 +01:00
|
|
|
elif isinstance(p, Property.Select):
|
2021-02-01 01:38:04 +01:00
|
|
|
return {"label": p.label, "type": "select", "configurable": True, "options": p.options,
|
|
|
|
"description": p.description}
|
2021-01-17 22:49:18 +01:00
|
|
|
elif isinstance(p, Property.Actor):
|
|
|
|
return {"label": p.label, "type": "actor", "configurable": p.configurable, "description": p.description}
|
|
|
|
elif isinstance(p, Property.Sensor):
|
|
|
|
return {"label": p.label, "type": "sensor", "configurable": p.configurable, "description": p.description}
|
|
|
|
elif isinstance(p, Property.Kettle):
|
|
|
|
return {"label": p.label, "type": "kettle", "configurable": p.configurable, "description": p.description}
|
2022-01-02 17:08:23 +01:00
|
|
|
elif isinstance(p, Property.Fermenter):
|
|
|
|
return {"label": p.label, "type": "fermenter", "configurable": p.configurable, "description": p.description}
|
2021-01-17 22:49:18 +01:00
|
|
|
|
|
|
|
def _parse_step_props(self, cls, name):
|
|
|
|
|
2021-01-22 23:25:20 +01:00
|
|
|
result = {"name": name, "class": cls, "properties": [], "actions": []}
|
2021-01-17 22:49:18 +01:00
|
|
|
|
|
|
|
if hasattr(cls, "cbpi_parameters"):
|
|
|
|
parameters = []
|
|
|
|
for p in cls.cbpi_parameters:
|
|
|
|
parameters.append(self._parse_property_object(p))
|
|
|
|
result["properties"] = parameters
|
|
|
|
for method_name, method in cls.__dict__.items():
|
|
|
|
if hasattr(method, "action"):
|
|
|
|
key = method.__getattribute__("key")
|
|
|
|
parameters = []
|
2021-02-01 01:38:04 +01:00
|
|
|
for p in method.__getattribute__("parameters"):
|
2021-01-17 22:49:18 +01:00
|
|
|
parameters.append(self._parse_property_object(p))
|
2022-02-26 21:45:10 +01:00
|
|
|
result["actions"].append(
|
|
|
|
{"method": method_name, "label": key, "parameters": parameters})
|
2021-01-17 22:49:18 +01:00
|
|
|
|
|
|
|
return result
|
|
|
|
|
2018-11-01 19:50:04 +01:00
|
|
|
def _parse_props(self, cls):
|
2018-12-29 00:27:19 +01:00
|
|
|
|
2018-11-01 19:50:04 +01:00
|
|
|
name = cls.__name__
|
|
|
|
|
|
|
|
result = {"name": name, "class": cls, "properties": [], "actions": []}
|
|
|
|
|
2019-07-27 21:08:19 +02:00
|
|
|
tmpObj = cls(cbpi=None, managed_fields=None)
|
2021-01-17 22:49:18 +01:00
|
|
|
members = [attr for attr in dir(tmpObj) if not callable(
|
|
|
|
getattr(tmpObj, attr)) and not attr.startswith("__")]
|
2018-11-01 19:50:04 +01:00
|
|
|
for m in members:
|
|
|
|
if isinstance(tmpObj.__getattribute__(m), Property.Number):
|
|
|
|
t = tmpObj.__getattribute__(m)
|
|
|
|
result["properties"].append(
|
2021-02-01 01:38:04 +01:00
|
|
|
{"name": m, "label": t.label, "type": "number", "configurable": t.configurable,
|
|
|
|
"description": t.description, "default_value": t.default_value})
|
2018-11-01 19:50:04 +01:00
|
|
|
elif isinstance(tmpObj.__getattribute__(m), Property.Text):
|
|
|
|
t = tmpObj.__getattribute__(m)
|
|
|
|
result["properties"].append(
|
2021-02-01 01:38:04 +01:00
|
|
|
{"name": m, "label": t.label, "type": "text", "configurable": t.configurable,
|
|
|
|
"default_value": t.default_value, "description": t.description})
|
2018-11-01 19:50:04 +01:00
|
|
|
elif isinstance(tmpObj.__getattribute__(m), Property.Select):
|
|
|
|
t = tmpObj.__getattribute__(m)
|
|
|
|
result["properties"].append(
|
2021-02-01 01:38:04 +01:00
|
|
|
{"name": m, "label": t.label, "type": "select", "configurable": True, "options": t.options,
|
|
|
|
"description": t.description})
|
2018-11-01 19:50:04 +01:00
|
|
|
elif isinstance(tmpObj.__getattribute__(m), Property.Actor):
|
|
|
|
t = tmpObj.__getattribute__(m)
|
2021-01-17 22:49:18 +01:00
|
|
|
result["properties"].append(
|
2021-02-01 01:38:04 +01:00
|
|
|
{"name": m, "label": t.label, "type": "actor", "configurable": t.configurable,
|
|
|
|
"description": t.description})
|
2018-11-01 19:50:04 +01:00
|
|
|
elif isinstance(tmpObj.__getattribute__(m), Property.Sensor):
|
|
|
|
t = tmpObj.__getattribute__(m)
|
2021-01-17 22:49:18 +01:00
|
|
|
result["properties"].append(
|
2021-02-01 01:38:04 +01:00
|
|
|
{"name": m, "label": t.label, "type": "sensor", "configurable": t.configurable,
|
|
|
|
"description": t.description})
|
2018-11-01 19:50:04 +01:00
|
|
|
elif isinstance(tmpObj.__getattribute__(m), Property.Kettle):
|
|
|
|
t = tmpObj.__getattribute__(m)
|
2021-01-17 22:49:18 +01:00
|
|
|
result["properties"].append(
|
2021-02-01 01:38:04 +01:00
|
|
|
{"name": m, "label": t.label, "type": "kettle", "configurable": t.configurable,
|
|
|
|
"description": t.description})
|
2022-01-02 17:08:23 +01:00
|
|
|
elif isinstance(tmpObj.__getattribute__(m), Property.Fermenter):
|
|
|
|
t = tmpObj.__getattribute__(m)
|
|
|
|
result["properties"].append(
|
|
|
|
{"name": m, "label": t.label, "type": "fermenter", "configurable": t.configurable,
|
|
|
|
"description": t.description})
|
2018-11-01 19:50:04 +01:00
|
|
|
|
|
|
|
for method_name, method in cls.__dict__.items():
|
2018-11-01 21:25:42 +01:00
|
|
|
if hasattr(method, "action"):
|
|
|
|
key = method.__getattribute__("key")
|
|
|
|
parameters = method.__getattribute__("parameters")
|
2021-01-17 22:49:18 +01:00
|
|
|
result["actions"].append(
|
|
|
|
{"method": method_name, "label": key, "parameters": parameters})
|
2018-12-29 00:27:19 +01:00
|
|
|
|
2019-07-27 21:08:19 +02:00
|
|
|
return result
|
2021-10-16 14:22:04 +02:00
|
|
|
|
2023-01-27 12:26:55 +01:00
|
|
|
async def load_plugin_list(self, filter="cbpi"):
|
2021-10-16 14:22:04 +02:00
|
|
|
result = []
|
|
|
|
try:
|
2022-02-26 21:45:10 +01:00
|
|
|
discovered_plugins = {
|
|
|
|
name: importlib.import_module(name)
|
|
|
|
for finder, name, ispkg
|
|
|
|
in pkgutil.iter_modules()
|
2023-01-27 12:26:55 +01:00
|
|
|
if name.startswith(filter) and len(name) > 4
|
2022-02-26 21:45:10 +01:00
|
|
|
}
|
|
|
|
for key, module in discovered_plugins.items():
|
|
|
|
from importlib.metadata import version
|
|
|
|
try:
|
|
|
|
from importlib.metadata import (distribution, metadata,
|
|
|
|
version)
|
|
|
|
meta = metadata(key)
|
|
|
|
result.append({row: meta[row]
|
|
|
|
for row in list(metadata(key))})
|
|
|
|
except Exception as e:
|
2022-02-28 11:15:02 +01:00
|
|
|
logger.error("FAILED to load plugin {} ".format(key))
|
2022-02-26 21:45:10 +01:00
|
|
|
logger.error(e)
|
|
|
|
|
2021-10-16 14:22:04 +02:00
|
|
|
except Exception as e:
|
2022-02-26 21:45:10 +01:00
|
|
|
logger.error(e)
|
2021-10-16 14:22:04 +02:00
|
|
|
return []
|
|
|
|
return result
|
2023-04-01 14:13:04 +02:00
|
|
|
|
|
|
|
async def load_plugin_names(self, filter="cbpi"):
|
|
|
|
result = []
|
2023-04-02 13:05:48 +02:00
|
|
|
result.append(dict(label="All", value="All"))
|
|
|
|
result.append(dict(label="craftbeerpi", value="craftbeerpi"))
|
|
|
|
result.append(dict(label="steps", value="steps"))
|
2023-04-01 14:13:04 +02:00
|
|
|
try:
|
|
|
|
discovered_plugins = {
|
|
|
|
name: importlib.import_module(name)
|
|
|
|
for finder, name, ispkg
|
|
|
|
in pkgutil.iter_modules()
|
|
|
|
if name.startswith('cbpi') and len(name) > 4
|
|
|
|
}
|
|
|
|
for key, module in discovered_plugins.items():
|
|
|
|
try:
|
|
|
|
meta = metadata(key)
|
2023-04-03 20:39:35 +02:00
|
|
|
if meta["Name"] != "cbpi4gui" and meta["Keywords"] == "globalsettings":
|
2023-04-02 13:05:48 +02:00
|
|
|
result.append(dict(label=meta["Name"], value=meta["Name"]))
|
2023-04-01 14:13:04 +02:00
|
|
|
except Exception as e:
|
2023-04-05 06:45:33 +02:00
|
|
|
logger.error("FAILED to read metadata for plugin {} ".format(key))
|
2023-04-01 14:13:04 +02:00
|
|
|
logger.error(e)
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
return result
|
|
|
|
return result
|