Need Help Converting script for use in Node-Red

Hi,

I am currently having trouble converting the script below written by @pb66 for use in Node-Red. If anyone is able to help me or guide me, it would be much appreciated. I am essentially after the output of script, which is numbers so I can put populate them into a graph for viewing. I am willing to pay if it can be done.

I am using the same meter as mentioned in the script. PZEM-016.

Thank you!

#!/usr/bin/env python

"""

Driver for the PZEM-014 and PZEM-016 Energy monitors, for communication using the Modbus RTU protocol.

"""

__author__  = "Paul Burnell"
__license__ = "TBC"

try:
    import minimalmodbus
except ImportError:
    print("Cannot import minimalmodbus, is it installed?\nExiting...")
    quit()

class pzem(minimalmodbus.Instrument):

    def __init__(self, serialPort, slaveAddress=1):
        """
        Create monitor device instance
        """
        minimalmodbus.Instrument.__init__(self, serialPort, slaveAddress)
        self.serial.timeout = 0.1
        self.serial.baudrate = 9600

    def getData(self):
        """
        Return array of [V, A, W, Wh, Hz, PF, alarm] values.
        """
        for i in range(5):
            try:
                data = self.read_registers(0,10,4)
                break
            except IOError as e:
                pass
            # except Exception as e:
            #     print(e)
        if 'data' in locals():
            return [
                round((data[0]*0.1),1),                    # Voltage(0.1V)
                round((data[1]*65536+data[2]*0.001),3),    # Current(0.001A)
                round((data[3]*65536+data[4]*0.1),1),      # Power(0.1W)
                round((data[5]*65536+data[6]*1),0),        # Energy(1Wh)
                round((data[7]*0.1),1),                    # Frequency(0.1Hz)
                round((data[8]*0.01),2),                   # Power Factor(0.01)
                int(data[9]>0)]                            # Alarm(1)
        else:
            return False

    def getVoltage(self):
        """
        Return the line voltage (0.1V precision).
        """
        return self.read_register(0,1,4)

    def getCurrent(self):
        """
        Return the load current (0.001A precision).
        """
        return round((self.read_long(1,4)*0.001),3)

    def getPower(self):
        """
        Return the real power (0.1W precision).
        """
        return round((self.read_long(3,4)*0.1),1)

    def getEnergy(self):
        """
        Return the real energy (1Wh precision).
        """
        return self.read_long(5,4)

    def getFrequency(self):
        """
        Return the line frequency (0.1Hz precision).
        """
        return self.read_register(7,1,4)

    def getPowerFactor(self):
        """Return the power factor (0.01 precision).
        """
        return self.read_register(8,2,4)

    def getAlarmStatus(self):
        """Return the alarm status (0 or 1 for True or False).
        """
        return 1 if self.read_register(9,0,4) == 65535 else 0

    def getAlarmThreshold(self):
        """
        Return the power alarm threshold (1W precision).
        """
        return self.read_register(1,0,3)

    def setAlarmThreshold(self, watts):
        """
        Set the power alarm threshold (1W precision).
        """
        # alarmMax = 23000        # 2300 max for pzem014 or 23000 max for pzem016
        # if int(watts) > 0 and int(watts) <= alarmMax:
        try:
            self.write_register(1,watts,0,6)
            return True
        except Exception as e:
            pass
        return False

    def setZeroEnergy(self):
        """
        Reset the energy accumulator total to zero.
        """
        try: 
            self._performCommand(66,'')
        except Exception as e:
            return False
        return True

    def setSlaveAddress(self, address):
        """
        Set a new slave address (1 to 247), initially set to 1 from factory.
        Each device must have a unique address, Max of 31 devices per network.
        """
        return self.write_register(2,address,0,6)

    #def setCalibration(self):
    #    return self._performCommand(61,'')

#class pzem014(pzem):
#class pzem016(pzem):

if __name__ == "__main__":
    
    import time

    serialPort = "COM7"

    # # # Using a single devive with factory default address of "1"
    # # pz = pzem(serialPort)

    # Using a slave address other than the factory default of "1".
    slaveAddress = 2
    pz = pzem(serialPort, slaveAddress)

    while True:
        print(' '.join(str(v) for v in [time.time(),pz.address]+pz.getData()))
        time.sleep(10)

    # #pz.setZeroEnergy()

    # #pz.setSlaveAddress(3)

    # print(pz.setAlarmThreshold(22099))

    # print(pz.getAlarmThreshold())

Ignore all the commented stuff at the bottom, that’s just where I have been trying different stuff, this is imported into the script I run, (I’ll post that to, but it needs another import too)

pzem.zip (3.0 KB)
[edit - original zip contained the pzem.pyc file instead of pzem.py in error, corrected now, although the later files have evolved a bit since originally posted]

Currently contains 3 files, pzem.py is the code above, timestamp.py is used to time the intervals and looping, the “read_pzems.py” is what I’m using to read multiple pzems on a 10s loop. It outputs like so as I intend it to send to an emonhub socket interfacer.

C:\Users\paulb\Desktop\wip\pzem>read_pzems.py
1532335960.0 2 249.5 0.0 0.0 0.0 50.0 0.0 0
1532335960.0 3 249.7 0.0 0.0 0.0 50.0 0.0 0
1532335970.0 2 249.6 0.0 0.0 0.0 50.0 0.0 0
1532335970.0 3 249.7 0.0 0.0 0.0 50.1 0.0 0
Exiting...

C:\Users\paulb\Desktop\wip\pzem>

Just to say I have never run an external script or program from node-red, just native JavaScript and the node.js supplied stuff.

There should be an “input from program” module, but…

Hi @Nick_Lestician, why node-red?