theyosh / TerrariumPI

Home automated terrarium/aquarium or other enclosed environment with a Raspberry Pi
https://terrarium.theyosh.nl
GNU General Public License v3.0
402 stars 97 forks source link

[FEATURE]: Support for DockerPi (52pi) 4 channel relay HAT #873

Closed ThunderClap86 closed 5 months ago

ThunderClap86 commented 6 months ago

Not a programmer in any way, and you're my crash course (if you couldn't tell). I have tried already to integrate into the externalswitch.py with the existing code. No luck so far with the current configurations. When I told my wife what I was planning on doing, she bought this relay HAT for me off of Amazon and I didn't realize until too late that its not fully supported. That's why I bought the Qwiic Quad Relay(which is now working beautifully). Just as a challenge for a second tank/terrarium, I was hoping to get support for this random hardware I now have.

DockerPi (52pi) 4 channel relay HAT - stackable 4 Channel Relay Hat Board For Raspberry Pi is one of the "docker pi" module. It has 4 channel relay that you can put it on your smart home project. This four-channel relay uses high-quality relay modules combined with the most popular microcontroller technology to communicate with the Raspberry Pi. You can use 4 modules stacked together making four layers, 16 relays. Switch the I2C address of the device through the DIP switch on the circuit board to ensure that the four-layer module gets different addresses, thus avoiding the problem of address duplication. The module is very fast, easy to install, and supports multi-language development, making it ideal for smart home projects.

_ ##################### I'm hoping that this thing is simple enough to compile. I am able to use i2cset -y 1 0x10 0x01 0x00/0xFF (ON/OFF) to trigger the relay manually, I just do not know how to make or edit an executable script that might make this work.

product link/wiki

##################### MY ATTEMPT:

#!/usr/bin/env python3

import smbus
import sys
import time

DEVICE_BUS = 1
DEVICE_ADDR = 0x10
bus = smbus.SMBus(DEVICE_BUS)

# Specify the DockerPi relay HAT channel numbers
relay_channels = [1, 2, 3, 4]

def activate_relay(channel):
    # Activate the specified relay channel
    bus.write_byte_data(DEVICE_ADDR, channel, 0xFF)
    print(f"Relay {channel} activated.")
    time.sleep(1)

def deactivate_relay(channel):
    # Deactivate the specified relay channel
    bus.write_byte_data(DEVICE_ADDR, channel, 0x00)
    print(f"Relay {channel} deactivated.")
    time.sleep(1)

if __name__ == "__main__":
    try:
        if len(sys.argv) == 2 and sys.argv[1].isdigit():
            relay_to_activate = int(sys.argv[1])

            if relay_to_activate in relay_channels:
                activate_relay(relay_to_activate)

                # Wait for a separate deactivation call
                while True:
                    deactivation_input = input("Enter 'deactivate' to turn off the relay: ")
                    if deactivation_input.lower() == 'deactivate':
                        deactivate_relay(relay_to_activate)
                        break
                    else:
                        print("Invalid input. Please enter 'deactivate'.")
            else:
                print("Invalid relay channel. Please provide a valid channel number (1-4).")

        else:
            print("Usage: python 52pi_4chRelay.py <relay_channel>")
            print("Example: python 52pi_4chRelay.py 1")

    except KeyboardInterrupt:
        print("Script interrupted by user.")
theyosh commented 6 months ago

Hi,

after reading the wiki of the hardware, I came with the following external relay code:

#!/usr/bin/env python3

# All installed python libraries are available in this custom script

import sys
from pathlib import Path
from datetime import datetime

DEBUG = False
if DEBUG:
  debug_file = Path("/tmp/terrariumpi_test_relay_script.txt")

# DockerPi 4 relay extra code
import smbus2
DEVICE_BUS = 1
DEVICE_ADDR = 0x10

bus = smbus2.SMBus(DEVICE_BUS)

def get_relay_number():
  return Path(__file__).stem.split('-')[1]

relay_number = get_relay_number()
# End DockerPi 4 relay extra code

# When there is no extra arguments, TerrariumPI expect a float value above 0.0 to return the current state.
# Return -1.0 when not supported, or want to duplicate an existing relay
if len(sys.argv) == 1:
    # Call your readout or current state code here....
    # In this demo no read out option, return default value of -1.0
    print("-1.0")

# Here we start to set the relay to the requested state: Off (=0) or On (>0)
else:
    try:
        value = int(float(sys.argv[1]))
    except Exception as ex:
        raise ValueError(f"Invalid input value. Should be a number between 0 and 100 including. Error {ex}")

    if not (0 <= value <= 100):
        raise ValueError("Invalid input value. Should be a number between 0 and 100 including")

    if value == 0:
        if DEBUG:
            debug_file.write_text(f"[{datetime.now()}] Toggle power switch to state OFF => {value}\n")

        # Call your code here to toggle the relay to OFF ....
        # Docker Pi 4 relay code
        bus.write_byte_data(DEVICE_ADDR, relay_number, 0x00)

    else:
        if DEBUG:
            debug_file.write_text(f"[{datetime.now()}] Toggle power switch to state ON or dimming value: => {value}\n")

        # Call your code here to toggle the relay to ON ....
        # Or when using it as a dimmer, use the `value` to set the dimmer
        # Docker Pi 4 relay code
        bus.write_byte_data(DEVICE_ADDR, relay_number, 0xFF)

Make sure you name the file something like dockerpi_4_relay-1.py where the -1.py is very important. The code uses the filename to find the relay to toggle. So when you have created a file called dockerpi_4_relay-1.py and make a symlink to dockerpi_4_relay-2.py, dockerpi_4_relay-3.py and dockerpi_4_relay-4.py you have one single file for all the 4 relays.

And the code can figure out based on the filename which relay it should control.

Your version has some user input, which is interactive, and that does not work with external relays.

ThunderClap86 commented 6 months ago

Thanks @theyosh! I created the symlink, all four of the .py files with the naming convention you suggested are in the /home/pi/TerrariumPI/hardware/relays/ directory, hope that was correct. I created the files with the touch dockerpi_4_relay-1(2,3,4).py commands, then symlinked them with ln -sf ./dockerpi_4_relay-1.py /home/pi/TerrariumPI/hardware/relay/dockerpi_4_relay-4.py (for each file 2-4) then made them executable chmod +x dockerpi_4_relay-1.py dockerpi_4_relay-2.py dockerpi_4_relay-3.py dockerpi_4_relay-4.py

ThunderClap86 commented 6 months ago

Just re-read the script relay section again here and I realized I had to place the symlink files in /home/pi/executable/script/. That has been corrected.

ThunderClap86 commented 6 months ago

MANUAL INSTALL - no Docker

With the files symlink in /home/pi/executable/script/, the configuration Screenshot 2024-01-05 172903 produces: image

theyosh commented 6 months ago

The address should be the full path of the script.

ThunderClap86 commented 6 months ago

Gotcha, got that entered. Address is now : /home/pi/executable/script/dockerpi_4_relay-1.py (Relay1). It went green, saved, and is not triggering my relay set. I really wish I understood this better to not be a bother.

To be clearer: The software acknowledges it is "triggering" the relay, is a green pop-up. The log is also saying its updated the trigger correctly. The physical hardware is not responding with my current configuration.

Just noticed you're in the Netherlands, hopefully you get some good sleep. Ontzettend bedankt.

theyosh commented 6 months ago

Hmm ok, then we are going to do some manual testing. So login to your PI and run the following commands:

  1. cd Terrariumpi
  2. source venv/bin/activate
  3. /home/pi/executable/script/dockerpi_4_relay-1.py 1

Now relay 1 should toggle on. Replace 1 with a 0 for off. If that does not work, I guess you will have an error. Post it here, and lets see what I can do.

ThunderClap86 commented 6 months ago

I'm at work until tomorrow, I will do some when I get off duty in the morning. Thanks again.

ThunderClap86 commented 5 months ago

Okay, sorry for the delay. Got it tested and it failed with your manual testing. Here is the output:

(venv) pi@lizard:~/TerrariumPI $ /home/pi/executable/script/dockerpi_4_relay-1.py 0
Traceback (most recent call last):
   File "/home/pi/executable/script/dockerpi_4_relay-1.py", line 49, in <module>
    bus.write_byte_data(DEVICE_ADDR, relay_number, 0x00)
   File "/home/pi/TerrariumPI/venv/lib/python3.9/site-packages/smbus2/smbus2.py", line 451, in write_byte_data
    msg = i2c_smbus_ioctl_data.create(
   File "/home/pi/TerrariumPI/venv/lib/python3.9/site-packages/smbus2/smbus2.py", line 145, in create
    return i2c_smbus_ioctl_data(
TypeError: an integer is required (got type str)

AND

/home/pi/executable/script/dockerpi_4_relay-1.py 1
Traceback (most recent call last):
  File "/home/pi/executable/script/dockerpi_4_relay-1.py", line 58, in <module>
    bus.write_byte_data(DEVICE_ADDR, relay_number, 0xFF)
  File "/home/pi/TerrariumPI/venv/lib/python3.9/site-packages/smbus2/smbus2.py", line 451, in write_byte_data
    msg = i2c_smbus_ioctl_data.create(
  File "/home/pi/TerrariumPI/venv/lib/python3.9/site-packages/smbus2/smbus2.py", line 145, in create
    return i2c_smbus_ioctl_data(
TypeError: an integer is required (got type str)
ThunderClap86 commented 5 months ago

I tried with "relay-1" through 4, changing only the last digit for the corresponding relay per .py file in the testing. They all return the TypeError: an integer is required (got type str)

theyosh commented 5 months ago

Change the following code:

def get_relay_number():
  return Path(__file__).stem.split('-')[1]

with

def get_relay_number():
  return int(Path(__file__).stem.split('-')[1])

Addint int() arround the Path command. That should return an integer and not a string.

And maybe better, try also change the number 1 to -1 in the same line. That should still work, but then it does not matter anymore if you use a - in your file name. As long as it ends with a number, it is good to go. That give you a bit more freedom to name your srcripts.

ThunderClap86 commented 5 months ago

I found the error!

#!/usr/bin/env python3

# All installed python libraries are available in this custom script

import sys
from pathlib import Path
from datetime import datetime

DEBUG = False
if DEBUG:
  debug_file = Path("/tmp/terrariumpi_test_relay_script.txt")

# DockerPi 4 relay extra code
import smbus2
DEVICE_BUS = 1
DEVICE_ADDR = 0x10

bus = smbus2.SMBus(DEVICE_BUS)

~~def get_relay_number():
  return Path(__file__).stem.split('-')[1]~~  #added int before path and now triggers. 

>>>>def get_relay_number():
 >>>>> return int(Path(__file__).stem.split('-')[1])

relay_number = get_relay_number()
# End DockerPi 4 relay extra code

# When there is no extra arguments, TerrariumPI expect a float value above 0.0 to return the current state.
# Return -1.0 when not supported, or want to duplicate an existing relay
if len(sys.argv) == 1:
    # Call your readout or current state code here....
    # In this demo no read out option, return default value of -1.0
    print("-1.0")

# Here we start to set the relay to the requested state: Off (=0) or On (>0)
else:
    try:
        value = int(float(sys.argv[1]))
    except Exception as ex:
        raise ValueError(f"Invalid input value. Should be a number between 0 and 100 including. Error {ex}")

    if not (0 <= value <= 100):
        raise ValueError("Invalid input value. Should be a number between 0 and 100 including")

    if value == 0:
        if DEBUG:
            debug_file.write_text(f"[{datetime.now()}] Toggle power switch to state OFF => {value}\n")

        # Call your code here to toggle the relay to OFF ....
        # Docker Pi 4 relay code
        bus.write_byte_data(DEVICE_ADDR, relay_number, 0x00)

    else:
        if DEBUG:
            debug_file.write_text(f"[{datetime.now()}] Toggle power switch to state ON or dimming value: => {value}\n")

        # Call your code here to toggle the relay to ON ....
        # Or when using it as a dimmer, use the `value` to set the dimmer
        # Docker Pi 4 relay code
        bus.write_byte_data(DEVICE_ADDR, relay_number, 0xFF)
theyosh commented 5 months ago
def get_relay_number():
  return Path(__file__).stem.split('-')[1]  #added int before path and now triggers. 
def get_relay_number():
  return int(Path(__file__).stem.split('-')[1])

Duplicate functions. Remove the first one.

ThunderClap86 commented 5 months ago

It was replaced without duplication, I was just showing the difference. My apologies for the confusion.

ThunderClap86 commented 5 months ago

And apparently strikethrough text does not work within the code section.

~~def get_relay_number(): return Path(file).stem.split('-')[1]~~ #added int before path and now triggers. def get_relay_number(): return int(Path(file).stem.split('-')[1])