Dependency install finishes, but still unmet afterwards

Hi,

I’ve been trying to set up my own little hydroponic pepper garden. The offline part is going pretty decent…

a picot from the inside of my tent! the first habanero is almost ready for tasting and I’m very excited!
9192F02A-B81B-4725-B1E9-9D1B88D493C9_1_105_c

Since the vacation is coming soon I also gotten around to the hardware part of automating everything. I’ve been soldering, attaching and a lot of reading from the tutorial. With some help of Kyle I got Mycodo running on a pi. PH/EC CO2 Humidity and temperatures are all working as expected and here’s what it looks like now:

Screenshot 2021-07-17 at 10.44.50

Since I’m a total noob in programming and coding this last part is where I get stuck! I have a Sparkfun I2C quad solid state relay board nicely boxed in the powerbox. I’m also trying to edit the grove multichannel relay to work with this one… there is quiet a nice api from the Sparkfun I’m trying to integrate… however when I install the dependency it isn’t to be found afterwards…

my code:

coding=utf-8

example_dummy_output.py - Example Output module

from flask_babel import lazy_gettext

from mycodo.outputs.base_output import AbstractOutput
from mycodo.utils.constraints_pass import constraints_pass_positive_value
from mycodo.databases.models import OutputChannel
from mycodo.utils.database import db_retrieve_table_daemon

Measurements

measurements_dict = {
0: {
‘measurement’: ‘duration_time’,
‘unit’: ‘s’,
},
1: {
‘measurement’: ‘duty_cycle’,
‘unit’: ‘percent’
},
2: {
‘measurement’: ‘duration_time’,
‘unit’: ‘s’,
},
3: {
‘measurement’: ‘duty_cycle’,
‘unit’: ‘percent’
},
4: {
‘measurement’: ‘duration_time’,
‘unit’: ‘s’,
},
5: {
‘measurement’: ‘duty_cycle’,
‘unit’: ‘percent’
},
6: {
‘measurement’: ‘duration_time’,
‘unit’: ‘s’,
},
7: {
‘measurement’: ‘duty_cycle’,
‘unit’: ‘percent’
}
}

channels_dict = {
1: {
‘name’: ‘Relay 1’,
‘types’: [‘on_off’,‘pwm’],
‘measurements’: [0,1]
},
2: {
‘name’: ‘Relay 2’,
‘types’: [‘on_off’,‘pwm’],
‘measurements’: [2,3]
},
3: {
‘name’: ‘Relay 3’,
‘types’: [‘on_off’,‘pwm’],
‘measurements’: [4,5]
},
4: {
‘name’: ‘Relay 4’,
‘types’: [‘on_off’,‘pwm’],
‘measurements’: [6,7]
}
}

Output information

OUTPUT_INFORMATION = {
# A unique output name used to distinguish it from others
‘output_name_unique’: ‘Sparkfun Qwiic Quad Solid state relay’,

# A friendly/common name for the output to display to the user
'output_name': 'Sparkfun Quad Solid State Relay',
'output_manufacturer': 'Sparkfun',
# Optional library name (for outputs that are named the same but use different libraries)


# The dictionary of measurements for this output. Don't edit this.
'measurements_dict': measurements_dict,
'channels_dict': channels_dict,
# Type of output. Options: "on_off", "pwm", "volume"
'output_types': ['on_off','pwm'],


'url_manufacturer': 'https://www.sparkfun.com/products/16833',
'url_datasheet': 'https://cdn.sparkfun.com/assets/learn_tutorials/1/1/8/6/SparkFun_Qwiic_Quad_Solid_State_Relay-Schematic.pdf',
'url_product_purchase': 'https://www.sparkfun.com/products/16833',

# A message to display at the top of the output options
'message': 'Controls the 2 or 4 channel Sparkfun solid state relay board.',

# Form input options that are enabled or disabled
'options_enabled': [

    'button_on',          # Shows a button to turn the output on
    'button_send_duty_cycle',
    'I2C location'
],
'options_disabled': [
    'interface'  # Show the interface (as a disabled input)
],

# Any dependencies required by the output module
'dependencies_module': [
    ('pip-pypi', 'sparkfun-qwiic-relay','sparkfun-qwiic-relay')
],

# The interface or interfaces that can be used with this module
# A custom interface can be used.
# Options: SHELL, PYTHON, GPIO, I2C, FTDI, UART
'interfaces': ['I2C'],
'i2c_location': ['0x08'],
'i2c_address_editable': True,
'i2c_address_default': '0x08',

'custom_channel_options': [
    {
        'id': 'name',
        'type': 'text',
        'default_value': '',
        'required': False,
    },
    {
        'id': 'state_startup',
        'type': 'select',
        'default_value': 0,
        'options_select': [
            (0, 'Off'),
            (1, 'On')
        ],
        'name': lazy_gettext('Startup State'),
        'phrase': 'Set the state of the relay when Mycodo starts'
    },
    {
        'id': 'state_shutdown',
        'type': 'select',
        'default_value': 0,
        'options_select': [
            (0, 'Off'),
            (1, 'On')
        ],
        'name': lazy_gettext('Shutdown State'),
        'phrase': 'Set the state of the relay when Mycodo shuts down'
    },
    {
        'id': 'on_state',
        'type': 'select',
        'default_value': 1,
        'options_select': [
            (1, 'HIGH'),
            (0, 'LOW')
        ],
        'name': lazy_gettext('On State'),
        'phrase': 'The state of the relay that corresponds to an On state'
    },
    {
        'id': 'trigger_functions_startup',
        'type': 'bool',
        'default_value': False,
        'name': lazy_gettext('Trigger Functions at Startup'),
        'phrase': 'Whether to trigger functions when the output switches at startup'
    },
    {
        'id': 'amps',
        'type': 'float',
        'default_value': 0.0,
        'required': True,
        'name': '{} ({})'.format(lazy_gettext('Current'), lazy_gettext('Amps')),
        'phrase': 'The current draw of the device being controlled'
    }
]

}

class OutputModule(AbstractOutput):
“”" An output support class that operates an output “”"
def init(self, output, testing=False):
super(OutputModule, self).init(output, testing=testing, name=name)

    self.myRelays = None

    output_channels = db_retrieve_table_daemon(OutputChannel).filter(OutputChannel.output_id == output.unique_id).all()
    self.options_channels = self.setup_custom_channel_options_json(
        OUTPUT_INFORMATION['custom_channel_options'], output_channels)

def setup_output(self):
    import qwiic_relay
    
    self.setup_output_variables(OUTPUT_INFORMATION)

    try:
        self.logger.debug("I2C: Address: {}".format(self.output.i2c_location))
        if self.output.i2c_location:
            ## Dit veranderen
            self.myRelays = qwiic_relay.QwiicRelay(i2c_location) ## klopt deze var verwijzing of moet dit ook self.output. zijn?
            self.output_setup = True
    except:
        self.logger.exception("Could not set up output")
        return

   

def output_switch(self,
                  state,
                  output_type=None,
                  amount=None,
                  duty_cycle=None,
                  output_channel=None):
    if output_channel is None:
        msg = "Output channel needs to be specified"
        self.logger.error(msg)
        return msg

    if not self.is_setup():
        msg = "Output not set up"
        self.logger.error(msg)
        return msg

    try:
        dict_states = {}
        for channel in channels_dict:
            if output_channel == channel:
                if state == 'On':
                    dict_states[channel] = bool(self.options_channels['on_state'][channel])
                    self.myRelays.set_relay_on(channel);
                elif state == 'Off':
                    dict_states[channel] = bool(not self.options_channels['on_state'][channel])
                    self.myRelays.set_relay_off(channel);
            else:
                dict_states[channel] = self.output_states[channel]

        self.logger.debug("List sent to device: {}".format(self.dict_to_list_states(dict_states)))
        # Dit veranderen
        self.output_states[output_channel] = dict_states[output_channel]
        msg = "success"
    except Exception as e:
        msg = "CH{} state change error: {}".format(output_channel, e)
        self.logger.error(msg)
    return msg

def is_on(self, output_channel=None):
    if self.is_setup():
        if output_channel is not None and output_channel in self.output_states:
            return self.output_states[output_channel] == self.options_channels['on_state'][output_channel]

def is_setup(self):
    return self.output_setup

@staticmethod
def dict_to_list_states(dict_states):
    list_states = []
    for i, _ in enumerate(dict_states):
        list_states.append(dict_states[i])
    return list_states

def stop_output(self):
    """ Called when Output is stopped """
    dict_states = {}
    if self.is_setup():
        for channel in channels_dict:
            if self.options_channels['state_shutdown'][channel] == 1:
                dict_states[channel] = bool(self.options_channels['on_state'][channel])
            elif self.options_channels['state_shutdown'][channel] == 0:
                dict_states[channel] = bool(not self.options_channels['on_state'][channel])
        self.logger.debug("List sent to device: {}".format(self.dict_to_list_states(dict_states)))
        self.myRelays.set_relay_off(self)
    self.running = False

the install log:
[2021-07-17 10:48:52] Dependency installation beginning. Installing: sparkfun-qwiic-relay

[2021-07-17 10:48:52]
[2021-07-17 10:48:52] #### Installing/updating sparkfun-qwiic-relay (pip-pypi)
[2021-07-17 10:48:53] Requirement already satisfied: sparkfun-qwiic-relay in ./env/lib/python3.7/site-packages (0.0.2)
[2021-07-17 10:48:53] Requirement already satisfied: sparkfun-qwiic-i2c in ./env/lib/python3.7/site-packages (from sparkfun-qwiic-relay) (0.9.11)
[2021-07-17 10:48:53] Requirement already satisfied: smbus2 in ./env/lib/python3.7/site-packages (from sparkfun-qwiic-i2c->sparkfun-qwiic-relay) (0.4.1)

[2021-07-17 10:48:55] End install of sparkfun-qwiic-relay

[2021-07-17 10:48:55]
[2021-07-17 10:48:55] #### Setting permissions

[2021-07-17 10:48:56] #### Dependency install finished

now there is probably a ton of other errors and there is still a lot to do (adding PWM) but I can’t test without trial and error :smiley:

Looks great! As for the dependency tuple, the second item needs to be the name of the module that’s imported:

('pip-pypi', 'qwiic-relay','sparkfun-qwiic-relay')
    ('pip-pypi', 'qwiic_relay','sparkfun-qwiic-relay') 

did it! dash or underscore… gosh, I’m gonna enjoy myself now haha thanks