Launch a customized sequence in python (with external input)

I have a customized sequence generated though the app and I would like to activate with a python script.

I found the command: fb.sequence(“CustumSequence”) , I need to put an external input.

How should I put the input variable for the sequence activation (xyz position).
What is the best solution?
Do you have some useful and practical documentation about?

I read Advanced | FarmBot Developer Documentation

Thanks,

Stefano

Hi @leonori

fb.sequence(“CustumSequence”)

is a new function in the FarmBot Python library (v2.0)
There is no way to pass an argument to that sequence from this function.

You could use pure Python code to send an RPC to the Farmbot or you could use the FarmBot Python library to call Lua code to execute your sequence with arguments.

fb.lua(' .. your Lua code to run a sequence with argument .. ')

fb.sequence(“CustumSequence”) is very limited. I will try to use python directly.
In a next step I want to incorporate everything with ROS2. I’m working on it.

Thank you very much!

That’s Ok, but you will need to create CeleryScript nodes from your Python code, I think.
Are you Ok doing that ?

p.s. In the FarmBot Python library package I opened this (minor) issue due to your question :slight_smile:

I’m trying to use fb.Sequence when it is possible and move the robot with python.
It should be easy to do.

However, I’m facing important issues in reading the current status of the robot.

I need to check if the sequence or command or generally speaking action is properly executed.
To do this I see that fb.get_xyz() does not properly work and thus I cannot check the current status.

I’m also trying to get the job state from given by the app but I don’t see any access to this information.

Can you show us what problem you are seeing ?
Are you using version 2.0.9 ( latest ) of FarmBot Python library ?

I just tested this and “it works on my PC” :wink:

from farmbot import Farmbot
TOKEN = . . .

fb = Farmbot()
fb.set_token(TOKEN)
fb.set_verbosity(0)
print(f'FarmBot Python library version {fb.__version__}')
pos = fb.get_xyz()
print(f'Location {pos}')

_

(fb_venv) jhs@rpi3b-64:~/fb-py $ python3 farmbot-pi.py
FarmBot Python library version 2.0.9
Location {'x': 1400.0, 'y': 1000.0, 'z': -50.0}
(fb_venv) jhs@rpi3b-64:~/fb2/fb-py $

Job information also comes out of the Bot State tree :christmas_tree:

Use fb.get_job(job_name: | None)

1 Like

Dear jsimmondds,

Thank you for your guidance.

I wanted to share an update on the code I am working on. It reads all the plant points and then samples the soil moisture one by one. To do this, I move the robot arm using Python code. I have also attached the implemented sequence; it’s quite simple.

To get the position, I decided to use local_position = fb.read_status(“location_data.position”), as it is the most stable method. The fb.move command is working well.

However, the soil sampling (see def playSequence(sequenceType, fb, plant)) works perfectly the first time but crashes during the second attempt. Specifically, the second time the program executes the sampling command soilSensor_value = fb.read_status(‘pins’)[‘59’][‘value’], it fails.

It’s worth mentioning that when I use debugging mode and manually launch the sampling command twice, it works on the second attempt.

I suspect that the issue is related to MQTT, and I am unsure if it can be resolved. Perhaps I need to introduce a delay or a different approach.

I believe that CeleryScript is not necessary at this stage. Do you agree? Otherwise, I am open to studying it further.

If you are interested, I can keep you updated and share my code for further review.
My ultimate goal is to integrate this code into ROS2.

import json
import time
import math
from tabnanny import check

import requests
import numpy as np
from PIL import Image
from io import BytesIO
import matplotlib.pyplot as plt
from farmbot import Farmbot

def playSequence(sequenceType, fb, plant):
    if sequenceType == 'soilSensor':
        fb.move(plant['x'], plant['y'], -375)
        time.sleep(3)
        while True:
            try:
                fb.sequence("Soil Sensor Stef")
                break  # Esce dal ciclo se l'input è valido
                print("OK WITH Soil Sensor Stef")
            except ValueError:
                print("ERROR WITH Soil Sensor Stef")
            time.sleep(10)
        attempt=0
        while True:
            #fb.listen_for_status_changes(duration=2)
            time.sleep(10)
            attempt+=1
            print("sensor_value Attempt", attempt)
            try:
                fb.clear_cache()
                time.sleep(10)
                soilSensor_value=fb.read_status('pins')['59']['value']
                print("sensor_value measure, fb.read_status('pins')['59']['value']")
                break
            except ValueError:
                print("ERROR WITH fb.read_status('pins')['59']['value']")
        fb.toast("fb.read_status('pins')['59']['value']", message_type="success")
        print('soil measure: ', soilSensor_value)
        return soilSensor_value

# Initialize the FarmBot class
email = "..."
password = "..."
fb = Farmbot()
token = fb.get_token(email, password)
fb.clear_cache()
fb.set_verbosity(0)
fb.connect_broker()

security_height=-270
fb.listen_for_status_changes(stop_count=3)
points = fb.api_get("points")
# Filter out only the plants
plants = [point for point in points if point["pointer_type"] == "Plant"]
sequenceType = 'soilSensor'
local_position = fb.read_status("location_data.position")
print('Local Position: ', local_position)

print('move to security_height: fb.move(',local_position['x'],',',local_position['y'],',',security_height,')')
fb.move(local_position['x'], local_position['y'], security_height)
fb.listen_for_status_changes(duration=5)
time.sleep(2)
soil_measures = []
i = 0
for plant in plants:
    fb.wait(1000)
    time.sleep(1)
    print('move above the plant: fb.move(', plant['x'], ',', plant['y'], ',', security_height, ')')
    fb.toast("move above the plant", message_type="success")
    fb.move(plant['x'], plant['y'], security_height)
    time.sleep(5)
    soil_measures.append(playSequence(sequenceType, fb, plant))
    print('soil measure success, move above the plant: fb.move(', plant['x'], ',', plant['y'], ',', security_height, ')')
    fb.toast("soil measure success, move above the plant", message_type="success")
    fb.move(plant['x'], plant['y'], security_height)
    time.sleep(5)
    i += 1

fb.sequence("Dismount Tool")

This is the error:

Traceback (most recent call last):
  File "F:\pyCharm\FarmBot\farmbot_plantsSoilMeasures.py", line 83, in <module>
    soil_measures.append(playSequence(sequenceType, fb, plant))
  File "F:\pyCharm\FarmBot\farmbot_plantsSoilMeasures.py", line 34, in playSequence
    soilSensor_value=fb.read_status('pins')['59']['value']
  File "F:\pyCharm\FarmBot\.venv\lib\site-packages\farmbot\main.py", line 180, in read_status
    return self.info.read_status(path)
  File "F:\pyCharm\FarmBot\.venv\lib\site-packages\farmbot\functions\information.py", line 169, in read_status
    status_tree = status_tree[key]
TypeError: 'NoneType' object is not subscriptable

I modified the line with:
soilSensor_value=fb.read_status(‘pins’)[‘59’][‘value’]

writing:
#soilSensor_value=fb.read_status(‘pins’)[‘59’][‘value’]
api_get_sens_readings = fb.api_get(‘sensor_readings’)
soilSensor_value=api_get_sens_readings[0][‘value’]

It worked, I don’t know why.

Hi @leonori that’s a perceptive comment and we can make things work correctly.
Did you notice those 3 log messages at 9:19am on the Web App
MQTT ACCESS DENIED Device is rate limited
They’ll be causing your code to crash because your code always assumes valid results from fb.

I’ll need more time to find a solution in your code . . .

With the correction I sent below the program works.
I want to test if CeleryScript works better.

1 Like

Seems FarmBot Python library v2.0.10 has a change which allows this :slight_smile:

Hi, I updated the library and ran the script below, but it’s not working. I don’t understand what kind of error I made. Could you help me figure it out?
The program does not crash but no toast message is published.

fb.sequence(‘Print Message Sequence’,cs_body=(
{
“kind”: “send_message”,
“args”: {
“message_type”: “success”,
“message”: “FarmBot è in posizione {{ x }}, {{ y }}, {{ z }}.”
},
“body”: [
{
“kind”: “channel”,
“args”: {
“channel_name”: “toast”
}
}
]
}
))

Immagine 2025-02-27 102119

Hi there @leonori,

If you’d like to generate a toast message from Python, the best way is to use the toast function:

fb.toast("FarmBot è in posizione {{ x }}, {{ y }}, {{ z }}.", message_type="success")

I see you’ve used the function in your script, so you should be familiar with it.

If you’d like to send CeleryScript that you write yourself you can use the publish command, but in most cases there’s a FarmBot Python library function using terminology similar to what you’d see in the FarmBot Web App that you can use instead (like the toast function).

Regarding the Python code you shared, fb.sequence('Print Message Sequence') will run the Sequence named “Print Message Sequence” that you have in the FarmBot Web App, which according to your screenshot already has the Send Message step, so any additional arguments are erroneous.

The change in the library with v2.0.10 allows adding variables when running a Sequence using the sequence function. Adding values of variables while running a Sequence from Python was previously only available using the publish function. The Python part of using it is shown in the screenshot @jsimmonds shared, and the Sequence named “Print Message Sequence” in the FarmBot Web App that it runs would have an externally defined text variable labeled “Text” and a Lua step that does something with the value of the text variable.

If you’d like to input an externally defined position, you can use the following Python code which supplies the value of a location variable to the Sequence run:

fb.sequence("My Sequence", cs_body=[
    {
      "kind": "parameter_application",
      "args": {
        "label": "My Location",
        "data_value": {
          "kind": "coordinate",
          "args": {
            "z": 3,
            "y": 2,
            "x": 1
          }
        }
      }
    }
  ]
)

The Sequence named “My Sequence” in the FarmBot Web App would have an externally defined location variable labeled “My Location” and Sequence steps that do something with that location (possibly a Lua step).

An additional tip:

Did you know that instead of fb.move(local_position['x'], local_position['y'], security_height), where local_position is the current position, you can use this?

fb.move(z=security_height)

You could also define your safe z height in the FarmBot Web App and just use safe_z on the next movement:

fb.move(plant['x'], plant['y'], safe_z=True)
3 Likes

Thank you, Gabriel! Now it’s working!
It seems to be useful for just sending inputs and, therefore, better generalizing custom sequences.

2 Likes