diff --git a/CHANGELOG.md b/CHANGELOG.md index 404568c4f..93478740f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ - Add Input Action: Execute Python 3 Code ([#1334](https://github.com/kizniche/Mycodo/issues/1334)) +### Bugfixes + + - Fix Actions not executing for MQTT, TTN, and Python Code Inputs ([#1336](https://github.com/kizniche/Mycodo/issues/1336)) + - Fix Python Code Inputs running pylint + ## 8.15.12 (2023.10.28) diff --git a/mycodo/actions/input_action_equation.py b/mycodo/actions/input_action_equation.py index d55d9db15..ea0f44436 100644 --- a/mycodo/actions/input_action_equation.py +++ b/mycodo/actions/input_action_equation.py @@ -81,7 +81,7 @@ def run_action(self, dict_vars): if original_value is None: msg = f" Error: No measurement found in dictionary passed to Action for channel {channel}." - self.logger.error(msg) + self.logger.debug(msg) dict_vars['message'] += msg return dict_vars diff --git a/mycodo/actions/input_action_mqtt_publish.py b/mycodo/actions/input_action_mqtt_publish.py index faae85b73..7db446cba 100644 --- a/mycodo/actions/input_action_mqtt_publish.py +++ b/mycodo/actions/input_action_mqtt_publish.py @@ -165,7 +165,7 @@ def run_action(self, dict_vars): self.logger.debug(f"Input channel: {channel}, payload: {payload}") if payload is None: - msg = f" Error: No measurement found in dictionary passed to Action for channel {channel}." + msg = f" Error: No measurement found in payload for {channel}." self.logger.error(msg) dict_vars["message"] += msg return dict_vars diff --git a/mycodo/controllers/controller_input.py b/mycodo/controllers/controller_input.py index 3e17fe1b5..20e28d0b6 100644 --- a/mycodo/controllers/controller_input.py +++ b/mycodo/controllers/controller_input.py @@ -26,11 +26,12 @@ import time from mycodo.controllers.base_controller import AbstractController -from mycodo.databases.models import (SMTP, Actions, Conversion, +from mycodo.databases.models import (SMTP, Conversion, DeviceMeasurements, Input, Misc, Output, OutputChannel) from mycodo.inputs.base_input import AbstractInput from mycodo.mycodo_client import DaemonControl +from mycodo.utils.actions import run_input_actions from mycodo.utils.database import db_retrieve_table_daemon from mycodo.utils.influx import add_measurements_influxdb from mycodo.utils.inputs import parse_input_information, parse_measurement @@ -218,23 +219,9 @@ def loop(self): if self.measurement_success: measurements_dict = self.create_measurements_dict() - # Run any actions + # Run actions message = "Executing actions of Input." - actions = db_retrieve_table_daemon(Actions).filter( - Actions.function_id == self.unique_id).all() - for each_action in actions: - return_dict = self.control.trigger_action( - each_action.unique_id, - value={"message": message, "measurements_dict": measurements_dict}, - debug=self.log_level_debug) - - # if message is returned, set message - if return_dict and 'message' in return_dict and return_dict['message']: - message = return_dict['message'] - - # if measurements_dict is returned, use that to store measurements - if return_dict and 'measurements_dict' in return_dict and return_dict['measurements_dict']: - measurements_dict = return_dict['measurements_dict'] + message, measurements_dict = run_input_actions(self.unique_id, message, measurements_dict, self.log_level_debug) # Add measurement(s) to influxdb use_same_timestamp = True diff --git a/mycodo/inputs/examples/example_all_options_temperature_with_channel_options.py b/mycodo/inputs/examples/example_all_options_temperature_with_channel_options.py index 84ba1bf5e..89b972cd1 100644 --- a/mycodo/inputs/examples/example_all_options_temperature_with_channel_options.py +++ b/mycodo/inputs/examples/example_all_options_temperature_with_channel_options.py @@ -3,6 +3,7 @@ from mycodo.databases.models import Conversion from mycodo.databases.models import InputChannel from mycodo.inputs.base_input import AbstractInput +from mycodo.utils.actions import run_input_actions from mycodo.utils.database import db_retrieve_table_daemon from mycodo.utils.influx import add_measurements_influxdb from mycodo.utils.inputs import parse_measurement @@ -333,9 +334,10 @@ def __init__(self, input_dev, testing=False): self.random = None self.interface = None - self.resolution = None self.i2c_address = None self.i2c_bus = None + self.resolution = None + self.log_level_debug = None # # Set variables to custom options @@ -374,6 +376,7 @@ def initialize(self): # self.resolution = self.input_dev.resolution + self.log_level_debug = self.input_dev.log_level_debug # # Initialize the sensor class @@ -441,6 +444,9 @@ def get_measurement(self): measurements[channel]['value'] = meas[channel]['value'] if measurements: + # Run Actions for Input before saving measurements to database + message, measurements = run_input_actions(self.unique_id, "", measurements, self.log_level_debug) + self.logger.debug("Adding measurements to influxdb: {}".format(measurements)) add_measurements_influxdb( self.unique_id, measurements, diff --git a/mycodo/inputs/mqtt_paho.py b/mycodo/inputs/mqtt_paho.py index 9476d0c31..c1b5210bc 100644 --- a/mycodo/inputs/mqtt_paho.py +++ b/mycodo/inputs/mqtt_paho.py @@ -7,6 +7,7 @@ from mycodo.databases.models import Conversion from mycodo.databases.models import InputChannel from mycodo.inputs.base_input import AbstractInput +from mycodo.utils.actions import run_input_actions from mycodo.utils.constraints_pass import constraints_pass_positive_value from mycodo.utils.database import db_retrieve_table_daemon from mycodo.utils.influx import add_measurements_influxdb @@ -154,6 +155,7 @@ class InputModule(AbstractInput): def __init__(self, input_dev, testing=False): super().__init__(input_dev, testing=testing, name=__name__) + self.log_level_debug = None self.client = None self.mqtt_hostname = None @@ -175,6 +177,8 @@ def __init__(self, input_dev, testing=False): def initialize(self): import paho.mqtt.client as mqtt + self.log_level_debug = self.input_dev.log_level_debug + input_channels = db_retrieve_table_daemon( InputChannel).filter(InputChannel.input_id == self.input_dev.unique_id).all() self.options_channels = self.setup_custom_channel_options_json( @@ -276,6 +280,8 @@ def on_message(self, client, userdata, msg): measurement = self.check_conversion(channel, measurement) if measurement: + message, measurement = run_input_actions(self.unique_id, "", measurement, self.log_level_debug) + self.logger.debug(f"Adding measurement to influxdb: {measurement}") add_measurements_influxdb( self.unique_id, diff --git a/mycodo/inputs/mqtt_paho_json.py b/mycodo/inputs/mqtt_paho_json.py index 87db33487..0873f3065 100644 --- a/mycodo/inputs/mqtt_paho_json.py +++ b/mycodo/inputs/mqtt_paho_json.py @@ -3,7 +3,7 @@ import json from flask_babel import lazy_gettext - +from mycodo.utils.actions import run_input_actions from mycodo.config_translations import TRANSLATIONS from mycodo.databases.models import Conversion from mycodo.databases.models import InputChannel @@ -170,6 +170,7 @@ class InputModule(AbstractInput): def __init__(self, input_dev, testing=False): super().__init__(input_dev, testing=testing, name=__name__) + self.log_level_debug = None self.client = None self.jmespath = None self.options_channels = None @@ -195,6 +196,7 @@ def initialize(self): import jmespath self.jmespath = jmespath + self.log_level_debug = self.input_dev.log_level_debug input_channels = db_retrieve_table_daemon( InputChannel).filter(InputChannel.input_id == self.input_dev.unique_id).all() @@ -307,7 +309,9 @@ def on_message(self, client, userdata, msg): self.logger.error( "Error in JSON '{}' finding '{}': {}".format( json_values, json_name, err)) - + + message, measurement = run_input_actions(self.unique_id, "", measurement, self.log_level_debug) + self.logger.debug(f"Adding measurement to influxdb: {measurement}") add_measurements_influxdb( self.unique_id, diff --git a/mycodo/inputs/python_code.py b/mycodo/inputs/python_code.py index c7219d1f5..a4d3c652b 100644 --- a/mycodo/inputs/python_code.py +++ b/mycodo/inputs/python_code.py @@ -25,6 +25,7 @@ def generate_code(new_input): sys.path.append(os.path.abspath('/var/mycodo-root')) from mycodo.databases.models import Conversion from mycodo.mycodo_client import DaemonControl +from mycodo.utils.actions import run_input_actions from mycodo.utils.database import db_retrieve_table_daemon from mycodo.utils.influx import add_measurements_influxdb from mycodo.utils.inputs import parse_measurement @@ -68,6 +69,8 @@ def store_measurement(self, channel=None, measurement=None, timestamp=None): measure[channel]['unit'] = meas_conv[channel]['unit'] measure[channel]['value'] = meas_conv[channel]['value'] + message, measure = run_input_actions(self.input_id, "", measure) + add_measurements_influxdb(self.input_id, measure) def python_code_run(self): @@ -150,7 +153,7 @@ def execute_at_modification( 'export PYLINTHOME=/var/mycodo-root/.pylint.d && ' \ '{dir}/env/bin/python -m pylint -d I,W0621,C0103,C0111,C0301,C0327,C0410,C0413 {path}'.format( dir=INSTALL_DIRECTORY, path=file_run) - cmd_out, cmd_error, cmd_status = cmd_output(cmd_test) + cmd_out, cmd_error, cmd_status = cmd_output(cmd_test, user='root') pylint_message = Markup( '
\n\n' 'Full Python Code Input code:\n\n{code}\n\n' diff --git a/mycodo/inputs/python_code_v_2_0.py b/mycodo/inputs/python_code_v_2_0.py index 2a8cdf640..70d4842e9 100644 --- a/mycodo/inputs/python_code_v_2_0.py +++ b/mycodo/inputs/python_code_v_2_0.py @@ -24,11 +24,7 @@ def generate_code(input_id, python_code): pre_statement_run = """import os import sys sys.path.append(os.path.abspath('/var/mycodo-root')) -from mycodo.databases.models import Conversion from mycodo.mycodo_client import DaemonControl -from mycodo.utils.database import db_retrieve_table_daemon -from mycodo.utils.influx import add_measurements_influxdb -from mycodo.utils.inputs import parse_measurement control = DaemonControl() class PythonInputRun: @@ -39,38 +35,6 @@ def __init__(self, logger, input_id, measurement_info, channels_conversion, chan self.channels_conversion = channels_conversion self.channels_measurement = channels_measurement - def store_measurement(self, channel=None, measurement=None, timestamp=None): - if None in [channel, measurement]: - return - measure = { - channel: { - 'measurement': self.measurement_info[channel]['measurement'], - 'unit': self.measurement_info[channel]['unit'], - 'value': measurement, - 'timestamp_utc': None - } - } - if timestamp: - measure[channel]['timestamp_utc'] = timestamp - - if channel in self.channels_conversion and self.channels_conversion[channel]: - conversion = db_retrieve_table_daemon( - Conversion, - unique_id=self.channels_measurement[channel].conversion_id) - if conversion: # Convert value/unit is conversion_id present and valid - meas_conv = parse_measurement( - self.channels_conversion[channel], - self.channels_measurement[channel], - measure, - channel, - measure[channel], - timestamp=measure[channel]['timestamp_utc']) - measure[channel]['measurement'] = meas_conv[channel]['measurement'] - measure[channel]['unit'] = meas_conv[channel]['unit'] - measure[channel]['value'] = meas_conv[channel]['value'] - - add_measurements_influxdb(self.input_id, measure) - def python_code_run(self): """ @@ -164,7 +128,7 @@ def execute_at_modification( 'export PYLINTHOME=/var/mycodo-root/.pylint.d && ' \ '{dir}/env/bin/python -m pylint -d I,W0621,C0103,C0111,C0301,C0327,C0410,C0413 {path}'.format( dir=INSTALL_DIRECTORY, path=file_run) - cmd_out, cmd_error, cmd_status = cmd_output(cmd_test) + cmd_out, cmd_error, cmd_status = cmd_output(cmd_test, user='root') pylint_message = Markup( '\n\n' 'Full Python Code Input code:\n\n{code}\n\n' @@ -173,6 +137,7 @@ def execute_at_modification( code=lines_code, report=cmd_out.decode("utf-8"))) except Exception as err: cmd_status = None + cmd_error = None messages["error"].append("Error running pylint: {}".format(err)) if cmd_status: @@ -206,7 +171,8 @@ def execute_at_modification( 'message': 'This is an alternate Python 3 Code Input that uses a different method for storing values to the ' 'database. This was created because the Python 3 Code v1.0 Input does not allow the use of Input ' - 'Actions. This method does allow the use of Input Actions. ' + 'Actions. This method does allow the use of Input Actions. (11/21/2023 Update: The Python 3 Code ' + '(v1.0) Input now allows the execution of Actions). ' 'All channels require a Measurement Unit to be selected and saved in order to store values to the ' 'database. Your code is executed from the same Python virtual environment that Mycodo runs from. ' 'Therefore, you must install Python libraries to this environment if you want them to be available to ' diff --git a/mycodo/inputs/ttn_data_storage.py b/mycodo/inputs/ttn_data_storage.py index a2135c966..b42cc988b 100644 --- a/mycodo/inputs/ttn_data_storage.py +++ b/mycodo/inputs/ttn_data_storage.py @@ -11,6 +11,7 @@ from mycodo.databases.models import InputChannel from mycodo.databases.utils import session_scope from mycodo.inputs.base_input import AbstractInput +from mycodo.utils.actions import run_input_actions from mycodo.utils.database import db_retrieve_table_daemon from mycodo.utils.influx import add_measurements_influxdb from mycodo.utils.inputs import parse_measurement @@ -124,6 +125,7 @@ class InputModule(AbstractInput): def __init__(self, input_dev, testing=False): super().__init__(input_dev, testing=testing, name=__name__) + self.log_level_debug = None self.first_run = True self.application_id = None @@ -136,6 +138,7 @@ def __init__(self, input_dev, testing=False): self.try_initialize() def initialize(self): + self.log_level_debug = self.input_dev.log_level_debug self.interface = self.input_dev.interface self.period = self.input_dev.period self.latest_datetime = self.input_dev.datetime @@ -211,6 +214,8 @@ def get_new_data(self, past_seconds): measurements[channel]['value'] = meas[channel]['value'] if measurements: + message, measurements = run_input_actions(self.unique_id, "", measurements, self.log_level_debug) + self.logger.debug("Adding measurements to influxdb: {}".format(measurements)) add_measurements_influxdb( self.unique_id, measurements, diff --git a/mycodo/inputs/ttn_data_storage_ttn_v3.py b/mycodo/inputs/ttn_data_storage_ttn_v3.py index 3630f10eb..2fdee49ad 100644 --- a/mycodo/inputs/ttn_data_storage_ttn_v3.py +++ b/mycodo/inputs/ttn_data_storage_ttn_v3.py @@ -12,6 +12,7 @@ from mycodo.databases.models import InputChannel from mycodo.databases.utils import session_scope from mycodo.inputs.base_input import AbstractInput +from mycodo.utils.actions import run_input_actions from mycodo.utils.database import db_retrieve_table_daemon from mycodo.utils.influx import add_measurements_influxdb from mycodo.utils.inputs import parse_measurement @@ -127,6 +128,7 @@ class InputModule(AbstractInput): def __init__(self, input_dev, testing=False): super().__init__(input_dev, testing=testing, name=__name__) + self.log_level_debug = None self.first_run = True self.application_id = None @@ -146,6 +148,7 @@ def __init__(self, input_dev, testing=False): self.try_initialize() def initialize(self): + self.log_level_debug = self.input_dev.log_level_debug self.interface = self.input_dev.interface self.period = self.input_dev.period self.latest_datetime = self.input_dev.datetime @@ -260,6 +263,8 @@ def get_new_data(self, past_seconds): measurements[channel]['value'] = meas[channel]['value'] if measurements: + message, measurements = run_input_actions(self.unique_id, "", measurements, self.log_level_debug) + self.logger.debug("Adding measurements to influxdb: {}".format(measurements)) add_measurements_influxdb( self.unique_id, measurements, diff --git a/mycodo/inputs/ttn_data_storage_ttn_v3_jmespath.py b/mycodo/inputs/ttn_data_storage_ttn_v3_jmespath.py index eef8289ea..a6a4dc9f6 100644 --- a/mycodo/inputs/ttn_data_storage_ttn_v3_jmespath.py +++ b/mycodo/inputs/ttn_data_storage_ttn_v3_jmespath.py @@ -12,6 +12,7 @@ from mycodo.databases.models import InputChannel from mycodo.databases.utils import session_scope from mycodo.inputs.base_input import AbstractInput +from mycodo.utils.actions import run_input_actions from mycodo.utils.database import db_retrieve_table_daemon from mycodo.utils.influx import add_measurements_influxdb from mycodo.utils.inputs import parse_measurement @@ -135,6 +136,7 @@ class InputModule(AbstractInput): def __init__(self, input_dev, testing=False): super().__init__(input_dev, testing=testing, name=__name__) + self.log_level_debug = None self.jmespath = None self.first_run = True @@ -159,6 +161,7 @@ def initialize(self): self.jmespath = jmespath + self.log_level_debug = self.input_dev.log_level_debug self.interface = self.input_dev.interface self.period = self.input_dev.period self.latest_datetime = self.input_dev.datetime @@ -286,6 +289,8 @@ def get_new_data(self, past_seconds): measurements[channel]['value'] = meas[channel]['value'] if measurements: + message, measurements = run_input_actions(self.unique_id, "", measurements, self.log_level_debug) + self.logger.debug("Adding measurements to influxdb: {}".format(measurements)) add_measurements_influxdb( self.unique_id, measurements, diff --git a/mycodo/mycodo_flask/templates/pages/input.html b/mycodo/mycodo_flask/templates/pages/input.html index f6a3445fb..3ffd7d676 100644 --- a/mycodo/mycodo_flask/templates/pages/input.html +++ b/mycodo/mycodo_flask/templates/pages/input.html @@ -240,7 +240,7 @@{{dict_translation['input']['title']}}