From 72c1dde7548754dd1aaad56e551ee2fc145d0a1f Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 21 Nov 2023 17:11:08 -0500 Subject: [PATCH] Fix Actions not executing for MQTT, TTN, and Python Code Inputs (#1336), Fix Python Code Inputs running pylint --- CHANGELOG.md | 5 +++ mycodo/actions/input_action_equation.py | 2 +- mycodo/actions/input_action_mqtt_publish.py | 2 +- mycodo/controllers/controller_input.py | 21 ++-------- ...ptions_temperature_with_channel_options.py | 8 +++- mycodo/inputs/mqtt_paho.py | 6 +++ mycodo/inputs/mqtt_paho_json.py | 8 +++- mycodo/inputs/python_code.py | 5 ++- mycodo/inputs/python_code_v_2_0.py | 42 ++----------------- mycodo/inputs/ttn_data_storage.py | 5 +++ mycodo/inputs/ttn_data_storage_ttn_v3.py | 5 +++ .../ttn_data_storage_ttn_v3_jmespath.py | 5 +++ .../mycodo_flask/templates/pages/input.html | 2 +- mycodo/scripts/upgrade_commands.sh | 1 + mycodo/utils/actions.py | 31 +++++++++++--- 15 files changed, 81 insertions(+), 67 deletions(-) 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']}}