Instant messaging with FarmBot

I’d love to create an instant messaging interface to FarmBot. I’d have it perform actions based on commands sent to it. FarmBot could also chat with me when it’s about to start or finish scheduled tasks, or send me pictures from its webcam.

I’ve built such chat interfaces before, and for FarmBot I’m looking for the best location to integrate this code. I think I should develop it as “FarmWare”, but it’s unclear to me where my code can “listen” to events.

Is there an API which I can use to hook my functions into? I don’t need FarmBot to send me a message for every log event, so I want to be able to specify which events to listen for and what to do when they occur.

3 Likes

Looking at some documentation of the API, it seems that it should be possible to add another “channel” for some messages to get pushed to. Where most logs are now being pushed to “toast” (which I believe is the popup messages that appear on screen), it’d be great if you could selectively push messages / logs to another channel. One which FarmWare can listen to.

@mdingena This is very doable, especially if you write it in NodeJS, since FarmBotJS works in node.

If you want to write it in another language, such as Java, Ruby, etc, this is also possible, as long as there are MQTT clients available. Let me know which language you plan on supporting and I can help you identify a client.

Here is how you could build a FarmBot chat app (high level overview):

Device -> IM client

  1. Generate an API token.
  2. Give the token to FarmbotJS -or- if you don’t want to use FarmBotJS, Point your MQTT client of choice to mqtt.farmbot.io with your email/token as credentials.
  3. Subscribe to the appropriate MQTT channel, which may differ based on your use case. Example: Subscribing to bot/device_23/logs would give you realtime logs for the device with an id of 23 (if your token was for device 23). Subscribing to bot/device_23/# will give you everything, unfiltered (useful if debugging). It will be a JSON object. See my note below about RPC formats.
  4. Tell your chat service to intercept relevant MQTT messages and forward them to whatever relevant chat protocol you use (IRC, XMPP, etc…)

IM -> Client

  1. Listen to the relevant IRC/XMPP/Whatever client.
  2. When an event is triggered, publish an RPC node to bot/device_23/from_clients (see my note below). This channel is reserved for inbound commanes

Note about RPC commands:

All Farmbot software (the frontend, FarmbotJS, the API, the MQTT broker, FBOS, etc…) shares a common format for sending/receiving realtime data and RPC. Internally, we have called it “CeleryScript”. A good example of CeleryScript usage is the sequence editor in the web app.

When you build a sequence in your browser, you are actually building a CeleryScript AST, which is later processed by FarmBotJS.

We don’t have any formal documentation for CeleryScript nodes yet for the simple reason that no one has asked. I can write such documentation if you require it. If you plan on using FarmBotJS, you will not need to dive into the specifics of CeleryScript since it is abstracted away. If you write the app in TypeScript, you will get intellisense / method documentation also.

Please let me know if that points you in the right direction. Like I mentioned, I would be more than willing to write documentation about the RPC commands if you end up not using the FarmBotJS client.

3 Likes

This is great stuff!

I’d prefer to write it in JavaScript, but ideally I’d like people to be able to enable or disable it as if it was FarmWare (like the Take a Photo FarmWare). Also, I’d like people to be able to use it within a Sequence, like sending an IM to my phone at the end of a sequence.

I took a look at the Take A Photo FarmWare and noticed it’s in Python. Can I still write my add-on in JavaScript?

2 Likes

@mdingena A couple of things:

  1. Sending messages to your phone: We have plans to eventually add more “channels” to the send_message block. Right now, the only channel we support is the status ticker, but we would like to eventually add other communication methods, such as email, etc…
  2. Javascript Farmwares: We support Python and MRuby (and possibly Elixir/Erlang) at the moment, but plan on adding JS support eventually.
  3. Farmware “Widgets”: Right now, the only Farmware that has a UI is the weed detector. We have plans to allow Farmwares to define their own widgets and sequence blocks, but at the moment, you would need your Farmware to send messages to something outside of the web app.

Like we mention in the readme, it’s still a very early feature that we rolled out only a month ago, and things can change quickly. Hope that helps answer your questions.

CC: @connor can you verify the list of languages we support for Firmware?

1 Like

Right now we support Python and Mruby (not mainline ruby)
Support for erlang could be added but is not currently enabled i dont think. (i doubt anyone wants to write a Farmware in Erlang)

Adding a JS runtime would be easy but just hasnt been done yet due to constraints on the root filesystem and other similar issues. We are working on a different system for things like this.

The problem comes to dependencies. Vanilla JS can be written and it would be fine, but if you wanted to pull in any dependency, then it gets a little more complex.
Once the problem of dependency management is solved there is an issue of cross compilation which is an entirely different beast.

the Farmware feature is still very early and very much in development.

1 Like

If I have to use an external program anyways, then I don’t want to bother with FarmWare at all. What I really need, then, is a websocket on which my program can listen for interesting messages.

Isn’t that what the frontend web app is doing now? Is it easy for me to write an external program and get access to those messages? So that when (for example) the message that “your FarmBot might be stuck!” comes through the socket, my program picks it up and can do its own thing with it? (like, send a push notification to my phone)

@mdingena
That is basically what the frontend is.
If you do prefer JavaScript, we have the farmbot-js library that is most of the backbone of communicating with the bot.
I’ll let @RickCarlino go into a bit more details, but its actually fairly simple to work with.

Okay, so maybe rather than work in parallel on a feature you might include in the future anyways, is there I way I can help develop FarmBot-JS to include, for example, Telegram integration?

Rick will have a better answer for you there,
But for right now i believe FarmbotJS is just a library, not an implementation, but if you have a well put together example, i think it would be a pretty good example for other developers.

We don’t have a featured projects yet but i believe someone was talking about it not to long ago.

Either way FarmbotJS is open source (just like the rest of the project) so you can definitely create Pull Requests we are always happy for help

@mdingena I started writing an example application that uses IRC. I used IRC since I’m already familiar with the protocol and don’t need to register for anything. This was my first time using FarmBotJS outside of the browser, and it turns out there were some small adjustments that need to be made to the sourcecode.

I will keep updating the example as my schedule permits and let you know when it’s fully functional. Feel free to poke around or submit PRs.

2 Likes

That looks really promising :smile:

Looking forward to playing with this.

Check out https://www.pubnub.com/ - loads of worked examples etc etc

@mdingena I have fixed all of the NodeJS issues with this latest release (3.9.3).
Full example at:

Please let me know if you hit any issues. Hope this helps!

I was just testing some code of my own. Unfortunately, my Farmbot isn’t always powered, and my WiFi coverage at the bot site is flaky, at best.

Would it be possible to test my code against an emulated Farmbot? Like, can I get a token which corresponds with a fake Farmbot rather than my own physical one?

While my own Farmbot was online, and reachable on my.farmbot.io, my app wasn’t able to connect to it.

// STEP ONE: Get an API token
axios
	.post(API_URL, config.farmbot.secret)
	.then(function (resp) {
		let token = resp.data.token.encoded;
		console.log(`Got API Token...`);
		console.dir(resp.data.token.unencoded)
		// STEP TWO: Connect to your FarmBot:
		let farmbot = new FarmBot({ token: token, secure: true });

		farmbot
			.connect()

@RickCarlino should I always request a new token for each operation, or should I store the token until it expires?

_getBotToken() {
	if( !this.config.farmbot.token ) {
		return axios.post( this.config.farmbot.url, this.config.farmbot.secret );
	} else {
		if( this.config.farmbot.token.unencoded.exp <= Math.floor( Date.now() / 1000 ) ) {
			return axios.post( this.config.farmbot.url, this.config.farmbot.secret );
		} else {
			let response = {
				data : {
					token : this.config.farmbot.token
				}
			};
			return new Promise.resolve( response );
		}
	}
} 

Something like that?

I’m still learning about Promises.

If FarmBotJS works with Promises (such as farmbot.connect().then(...) and I’m supposed to re-use the token as long as I can, which logic should I use to make sure my app is still connected to the bot?

I can’t wrap my head around it.

  • My app starts, has no token and no connection.
  • I try to move an axis, now app must check for token and connection.
  • App requests token, receives promise.
  • Promise resolves, now app can connect.
  • App is connected, now it can move axis.
  • I try to move axis again.
  • App requests token? Probably better to re-use current token if not yet expired, so return Promise.resolve(old_token).
  • Promise resolves, app connects again? Probably not necessary, but I don’t know how to check if bot is connected already or not (other than setting a boolean somewhere and listening for disconnect events).

Also, if the token expires, do I need to re-connect to the bot? I’m asking, since the token is passed to the constructor, and might expire while the app is still using that instance.

Please halp.

@mdingena Promises are definitely a tricky one to learn. The nice part of it all is that they are being adopted as a standard by almost all JS libraries and APIs, so once you learn the syntax, the knowledge transfers easily between projects.

A couple of things:

  1. You can generate a new token every time you start the app (or connect to MQTT) if that is easier for you. You currently don’t need to reconnect if the token expires, but will need to generate a new one to get back in if your connection to MQTT drops.
  2. Once you are connected to MQTT, you can send multiple requests. There is no need to connect multiple times to send multiple commands. This is unlike AJAX, where you must start a new connection for every request. You will need to wait for the command to be acknowledged by the distant end using .then(), but that is happening on the same connection. If you don’t perform sequences of requests via .then() chaining, your messages will execute randomly and out of order, just the same as with AJAX in a browser.

@RickCarlino Well, let’s say I want to move my Farmbot via instant messaging twice. There is 15 minutes between each command. My application is running indefinitely, listening for messages on the Telegram socket.

My first command comes in, and my app has already a token and performed farmbot.connect().

How do I proceed from that situation? The connection is still going I assume? Or it the connection closed after you’ve resolved all promises in the chain after .connect()?

Then, 15 minutes later, my second command comes in. Let’s pretend my token has now expired but I still haven’t closed the connection (and it hasn’t dropped either). Where to go from that situation?

Can I just request token once, connect once, and then send move commands arbitrarily? I need some sort of verification before attempting each move to ensure I’m still connected, right?