2-State Widget works in app but not website dashboard

Good afternoon,

I am publishing 1 or 0 to a couple 2-state widgets using mqtt in a python script on a RPi. When keeping the dashboard on the website the state shows correct for a second before it returns to “off” state and never changes even after the script sends additional value updates. When viewing in the Android app the dashboard behaves as expected.

Is this a known bug or a mistake I’m making in my code?

Thanks,

Adam

can you share the code you are using.

Here is the complete program which runs my pool and controls the heat. It has a couple of sensors for daylight and pump pressure.

 #! /usr/bin/env python3
    import os
    import cayenne.client
    import time
    import datetime
    import sys
    import glob
    import logging
    import gpiozero
    import busio
    import board
    from RPLCD.i2c import CharLCD
    from gpiozero import Button, OutputDevice
    from signal import pause

    # import adafruit_ads1x15.ads1015 as ADS
    import adafruit_ads1x15.ads1115 as ADS
    from adafruit_ads1x15.analog_in import AnalogIn

    # Temperature check. Start Heater if temp < HEATER_ON
    HEATER_ON_TEMP = 86
    # Temperature check. Shut down over HEATER_OFF
    HEATER_OFF_TEMP = 88

    #msg_HeaterControl = "Loading..."

    # *************************************************************************
    lcd = CharLCD('PCF8574', 0x27)

    # Cayenne Connection Information
    MQTT_USERNAME  = " "  
    MQTT_PASSWORD  = " "  
    MQTT_CLIENT_ID = " "

    # Initialize the GPIO Pins
    os.system('modprobe w1-gpio')  # Turns on the GPIO module
    os.system('modprobe w1-therm') # Turns on the Temperature module

    # Finds the correct device file that holds the temperature data
    base_dir = '/sys/bus/w1/devices/'
    device_folder_pool = glob.glob(base_dir + '28-01162136d3ee')[0]
    device_folder_heater = glob.glob(base_dir + '28-011623b89cee')[0]
    device_folder_air = glob.glob(base_dir + '28-02161de554ee')[0]
    device_file_pool = device_folder_pool + '/w1_slave'
    device_file_heater = device_folder_heater + '/w1_slave'
    device_file_air = device_folder_air + '/w1_slave'

    PumpRelayPin = 17 # Originally 17
    HeaterRelayPin = 22
    LightRelayPin = 23 # Originally 23

    PumpRelay = gpiozero.OutputDevice(PumpRelayPin, active_high=True, initial_value=True)
    HeaterRelay = gpiozero.OutputDevice(HeaterRelayPin, active_high=False, initial_value=False)
    LightRelay = gpiozero.OutputDevice(LightRelayPin, active_high=False, initial_value=False)

    #********* LED Button GPIO Setup *******************
    BlueButton=Button(18) # Declaring Blue button GPIO 18
    RedButton=Button(27) # Declaring Red button GPIO 27

    # Get what action. If you manually turning on/off the heater
    action = sys.argv.pop()

    # The callback for when a message is received from Cayenne.  
    def on_message(message):  
      print("message received: " + str(message))  
      msg_list = ast.literal_eval(str(message))  
      msg_value = msg_list['value']  
      msg_channel = msg_list['channel']  
      print("value is " + msg_value)  
      print("channel is %s" % msg_channel)  
      
      # Initialize the client  
    client = cayenne.client.CayenneMQTTClient()  
    client.on_message = on_message  
    client.begin(MQTT_USERNAME, MQTT_PASSWORD, MQTT_CLIENT_ID)  
      

    # Create the I2C bus
    i2c = busio.I2C(board.SCL, board.SDA)

    # Create the ADS object
    # ads = ADS.ADS1015(i2c)
    ads0 = ADS.ADS1115(i2c)
    ads1 = ADS.ADS1115(i2c)

    # Create a sinlge ended channel on Pin 0
    # Max counts for ADS1015 = 2047
    #                  ADS1115 = 32767

    chan0 = AnalogIn(ads0, ADS.P0) # Pressure
    chan1 = AnalogIn(ads1, ADS.P1) # Light

    # The ADS1015 and ADS1115 both have the same gain options.
    #
    #       GAIN    RANGE (V)
    #       ----    ---------
    #        2/3    +/- 6.144
    #          1    +/- 4.096
    #          2    +/- 2.048
    #          4    +/- 1.024
    #          8    +/- 0.512
    #         16    +/- 0.256
    #
    gain0 = (2 / 3)
    gain1 = (1)


    # A function that reads the sensors data
    def read_temp_raw():
    	f = open(device_file_pool, 'r') # Opens the temperature device file
    	lines = f.readlines() # Returns the text
    	f.close()
    	return lines
     
    def read_Heater_Output_temp_raw():
    	f = open(device_file_heater, 'r') # Opens the temperature device file
    	lines = f.readlines() # Returns the text
    	f.close()
    	return lines
    	
    def read_Air_temp_raw():
    	f = open(device_file_air, 'r') # Opens the temperature device file
    	lines = f.readlines() # Returns the text
    	f.close()
    	return lines

     
    # Convert the value of the sensor into a temperature
    def read_pool_temp():

    	lines = read_temp_raw() # Read the temperature 'device file'

    	# While the first line does not contain 'YES', wait for 0.2s
    	# and then read the device file again.
    	while lines[0].strip()[-3:] != 'YES':
    		time.sleep(0.2)
    		lines = read_temp_raw()
     
    	# Look for the position of the '=' in the second line of the
    	# device file.
    	equals_pos = lines[1].find('t=')
     
    	# If the '=' is found, convert the rest of the line after the
    	# '=' into degrees Celsius, then degrees Fahrenheit
    	if equals_pos != -1:
    		temp_string = lines[1][equals_pos+2:]
    		temp_c = float(temp_string) / 1000.0
    		temp_f = temp_c * 9.0 / 5.0 + 32.0
    		return temp_f
    	
    	
    def read_Heater_Output_temp():

    	lines = read_Heater_Output_temp_raw() # Read the temperature 'device file'

    	# While the first line does not contain 'YES', wait for 0.2s
    	# and then read the device file again.
    	while lines[0].strip()[-3:] != 'YES':
    		time.sleep(0.2)
    		lines = read_Heater_Output_temp_raw()
     
    	# Look for the position of the '=' in the second line of the
    	# device file.
    	equals_pos = lines[1].find('t=')
     
    	# If the '=' is found, convert the rest of the line after the
    	# '=' into degrees Celsius, then degrees Fahrenheit
    	if equals_pos != -1:
    		temp_string = lines[1][equals_pos+2:]
    		temp_c = float(temp_string) / 1000.0
    		temp_f = temp_c * 9.0 / 5.0 + 32.0
    		return temp_f	
    	
    def read_Air_temp():

    	lines = read_Air_temp_raw() # Read the temperature 'device file'

    	# While the first line does not contain 'YES', wait for 0.2s
    	# and then read the device file again.
    	while lines[0].strip()[-3:] != 'YES':
    		time.sleep(0.2)
    		lines = read_Air_temp_raw_raw()
     
    	# Look for the position of the '=' in the second line of the
    	# device file.
    	equals_pos = lines[1].find('t=')
     
    	# If the '=' is found, convert the rest of the line after the
    	# '=' into degrees Celsius, then degrees Fahrenheit
    	if equals_pos != -1:
    		temp_string = lines[1][equals_pos+2:]
    		temp_c = float(temp_string) / 1000.0
    		temp_f = temp_c * 9.0 / 5.0 + 32.0
    		return temp_f	
    	
    def Pump_on():
    	print("Pump On")
    	PumpRelay.on()
    	
    def Pump_off():
    	print("Pump and Heater Off")
    	HeaterRelay.off()
    	time.sleep(10)
    	PumpRelay.off()
    	
    def Heater_on():
    	print("Heater On")
    	HeaterRelay.on()
    	client.virtualWrite(18, 1, "digital_sensor")
    	
    	
    def Heater_off():
    	print("Heater Off")
    	HeaterRelay.off()
    	client.virtualWrite(18, 0, "digital_sensor")
    	
    def PoolLight_on():
    	print("Light On")
    	LightRelay.on()
    	client.virtualWrite(20, 1, "digital_sensor")
    	
    	
    def PoolLight_off():
    	print("Light Off")
    	LightRelay.off()
    	client.virtualWrite(20, 0, "digital_sensor")

    # def check_heater(pin):
    	# GPIOsetup()
    	# return GPIO.input(pin)


    def run_Heater():
    	#log file information
    	logging.basicConfig(filename='/home/pi/HydroPi/HeaterRelay.log',level=logging.INFO,format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
    	current_date = datetime.datetime.now()
    	global temp_pool
    	temp_pool = read_pool_temp()
    	temp_HeaterOutput = read_Heater_Output_temp()
    	temp_AirTemp = read_Air_temp()

    	global msg_HeaterControl			
    			
    	print('Temperature' +str(temp_pool))
    	client.virtualWrite(1, temp_pool, "analog_sensor")
    	client.virtualWrite(2, temp_HeaterOutput, "analog_sensor")
    	client.virtualWrite(25, temp_AirTemp, "analog_sensor")
    	
    	if float(temp_pool) <= HEATER_ON_TEMP:
    		if RedButton.is_pressed:
    			print ('Requesting Heater but Switch is Pressed')
    			msg_HeaterControl = "Button Override"
    			#time.sleep(10)
    		elif PumpRelay.value == 0: # Pump relay uses inverted logic
    			print ('Requesting Heater but Pump is Off')
    			msg_HeaterControl = "Pump Off Override"
    			#time.sleep(10)
    		else:
    			print('Heater ON')
    			logging.info("HEATER ON")
    			msg_HeaterControl = "Heating"
    			Heater_on()
    			#time.sleep(10)
    	elif float(temp_pool) >= HEATER_OFF_TEMP:
    		print('Turning OFF Heater')
    		logging.info("Turning off Heater")
    		msg_HeaterControl = "Heater Off"
    		Heater_off()
    		#time.sleep(10)
    		# return 1 # exit script. The pi has cooled down
    	else:
    		print('Pool is operating under normal temperatures.')
    		logging.info("Pool is operating under normal temperatures.")
    		msg_HeaterControl = "Normal Temperature"
    		#Heater_off()
    		#time.sleep(10)
    	

    def PoolPressure():
    	global psi
    	ads0.gain = gain0
    	volts = chan0.value / 32767.0 * 6.144
    	psi = 7.5 * volts - 3.75
    	#Print PSI
    	print("\n{0:0.2f} psi ".format(psi))
    	
    	client.virtualWrite(4, psi, "analog_sensor")

    def DaylightSensor():
    	#Print Light Value
    	print('LDR raw value: ' + str(chan1.value))
    	
    	#if value < 6000: it is now dark, turn LED on
    	if chan1.value < 6000:
    		print ("Its Sunny")
    		PoolLight_off()
    		lightsensor = "Sunny"
    	else:
    		print ("Its Dark")
    		PoolLight_on()
    		lightsensor = "Dark"
    	
    	client.virtualWrite(15, chan1.value, "analog_sensor")

    def LCD_Display():
    	lcdTemp = read_pool_temp
    	#heater = msg_HeaterControl
    	lcd.clear()
    	lcd.cursor_pos = (0, 0)
    	lcd.write_string(msg_HeaterControl)
    	lcd.cursor_pos = (1, 0)
    	lcd.write_string("Temp:{0:0.0f}".format(temp_pool) + " PSI:{0:0.1f}".format(psi))
    	

    	

    BlueButton.when_pressed = Pump_off
    BlueButton.when_released = Pump_on
    	
    RedButton.when_pressed = Heater_off
    RedButton.when_released = Heater_on
    		
    		
    while True:
    		if BlueButton.is_pressed:
    			PumpRelay.off()
    			time.sleep(5)
    			lcd.clear()
    			print ('Blue Button Pressed')
    			lcd.write_string("Pump Off")
    			client.virtualWrite(16, 0, "digital_sensor")
    		else:
    			client.loop()
    			client.virtualWrite(16, 1, "digital_sensor")
    			run_Heater()
    			PoolPressure()
    			DaylightSensor()
    			LCD_Display()
    			time.sleep(10)
    			
    			# Cayenne Connection
    			# Publish the change of state of the Sunday1 button which is on dashboard channel 4 
    			# Cayenne.virtualWrite(1, "[2,1,3]", "accel", "g");
    			 

    			time.sleep(5)
    	
    # except KeyboardInterrupt:  
        # # # here you put any code you want to run before the program   
        # # # exits when you press CTRL+C  
    		# logging.warning("Keyboard Interupted")  
    		
    # except SystemExit:  
        # # # here you put any code you want to run before the program   
        # # # exits when you press CTRL+C  
    		# logging.warning("System Error")
    		# lcd.clear()
    		# lcd.write_string("*** SYSTEM ERROR ***")
    # finally:  
       # # #GPIO.cleanup() # this ensures a clean exit
    		# logging.info("Program Exit")

one thing i noticed is, you are not adding a unit_value to the client.virtualWrite. Have a look at this Data types for Cayenne MQTT API and add the appropriate unit_value to it. You might want to delete the previously created 2 state sensor widget from the dashboard and let the code auto populate green widget on the dashboard. Add them by clicking on “+”.

That seems to have done the trick!

Thank-you!

2 Likes