Smarter Coffee Machine Control with TCP Binding

Hi All,

I am trying to control my Smarter Coffee maker from openhab using the TCP binding. The coffee maker accepts hex commands such as “0x35 0x01 0x7e” (set strength to medium) on port 2081.

I have implemented the following item, and mapping for the strength but I keep getting the error message '[t.i.s.MapTransformationService] - Could not find a mapping for ‘2’ in the file ‘coffeeStrength.map’ '.

Item

Number Coffee_Strength "Coffee Strength [MAP(coffee.map):%s]" <coffee> (Coffee) {tcp=">[192.168.0.3:2081:'MAP(coffeeStrength.map)'] " }

Mapping (coffee.map)

0=Weak
1=Medium
2=Strong

Mapping (coffeeStrength.map)

0=\u0035\u0000\u007e
1=\u0035\u0001\u007e
2=\u0035\u0002\u007e

I have also changed the character set to UTF-8 and postamble to \r in the config setup to no avail.

Can anybody help point me in the right direction?

Many thanks!

Just realised the ‘2’ that openhab is trying to transform is the beginning of the heartbeat the coffee machine sends every 5 seconds…

What is this Smarter Coffee machine you speak of? :slight_smile:

I found that the postamble was never being transmitted for me. I went ahead and added the \r to the end of my variable in the ,map file and everything started to work.

0=\u0035\u0000\u007e\r

I also made sure the postamble was commented out in the cfg file.

Hope that works for you.

The coffee machine is manufactured by a company called Smarter and is a bean to cup/pot machine; they also manufacture kettles that can be controlled through wifi.
The communication protocol is (mostly) documented at https://github.com/Jamstah/libsmarteram2/wiki/Protocol-documentation - bar one of the most important, brew! (0x37)

Thanks KidSquid, I seem to be getting a bit further now I’m sending \r at the end of my commands!

I have changed tactics a bit this morning and I now have a binding set up to send commands to the coffee maker. This will send all commands eg. change number of cups, strength etc. and (eventually) to read it’s heartbeat status messages. However I can’t get it to react to a message that I have generated eg 0x36 0x05 0x7E using the following rule, but the handcoded string does.

I’m thinking it’s something to do with character encoding but not sure how to solve it!

import org.joda.time.*
import org.openhab.core.library.types.*
import org.openhab.core.library.types.PercentType
import org.openhab.core.library.items.SwitchItem
import org.openhab.model.script.actions.*
import org.openhab.model.script.actions.Timer
import java.lang.*
import java.util.*


var Number cups

rule "Change cups"
when
	Item Coffee_Cups changed
then

cups = Coffee_Cups.state

var String cupsCmdHead = "\\u0036\\u000"
var String cupsCmdTail = "\\u007e\\r"
var String test = "\u0036\u0002\u007e\r"

var String cupsCmd = cupsCmdHead + String::format("%02X", cups.intValue) + cupsCmdTail

logInfo("Smarter Coffee", "{}", cupsCmd)

sendCommand(Coffee_CmdText, test)

end

Just on inspection your cupsCmdHead won’t be valid encoding because you’ve missed a byte, did you mean “\u0036\u0002”?

If that’s not the issue, does combining them in a single constructor work?

var String cupsCmd = new String("\u0036\u0002" + String::format("%02X", cups.intValue) + "\u0036\u0002\u007e\r")

I’ve managed to get this working using a series of if statements in between my post and now…just the status messages to look at.

However I gave your suggestion a go Ben - using the following constructor;

var String tcupsCmd = new String("\u0036\u00" + String::format("%02X", cups.intValue) + "\u007e\r")

This gives the error message

[ERROR] [t.protocol.internal.UDPBinding] - transformation throws exception [transformation=null, response=~]
java.lang.NullPointerException: null
        at java.util.regex.Matcher.getTextLength(Matcher.java:1234) ~[na:1.7.0_79]
        at java.util.regex.Matcher.reset(Matcher.java:308) ~[na:1.7.0_79]
        at java.util.regex.Matcher.<init>(Matcher.java:228) ~[na:1.7.0_79]
        at java.util.regex.Pattern.matcher(Pattern.java:1088) ~[na:1.7.0_79]
        at org.openhab.binding.tcp.protocol.internal.UDPBinding.splitTransformationConfig(UDPBinding.java:236) [bundlefile:na]
        at org.openhab.binding.tcp.protocol.internal.UDPBinding.transformResponse(UDPBinding.java:254) [bundlefile:na]
        at org.openhab.binding.tcp.protocol.internal.UDPBinding.parseBuffer(UDPBinding.java:150) [bundlefile:na]
        at org.openhab.binding.tcp.AbstractDatagramChannelBinding.parseChanneledBuffer(AbstractDatagramChannelBinding.java:998) [bundlefile:na]
        at org.openhab.binding.tcp.AbstractDatagramChannelBinding.execute(AbstractDatagramChannelBinding.java:1505) [bundlefile:na]
        at org.openhab.core.binding.AbstractActiveBinding$BindingActiveService.execute(AbstractActiveBinding.java:156) [org.openhab.core_1.8.3.jar:na]
        at org.openhab.core.service.AbstractActiveService$RefreshThread.run(AbstractActiveService.java:173) [org.openhab.core_1.8.3.jar:na]

I understand what you were originally trying to do a little more clearly now, sorry for the misinterpretation, would you mind posting what you got working? I don’t have the Smarter Coffee Machine, but I have been trying to get something working through the TCP binding with similar strings.

Hope these help!

/* Coffee Machine */
Number Coffee_Strength "Coffee Strength [MAP(coffee.map):%s]" <coffee> (Coffee)
Number Coffee_Cups "Cups to brew [%d]" <cup> (Coffee) 
Switch Coffee_Command "Brew coffee" <coffee> (Coffee)
String Coffee_CmdText (Coffee) {udp=">[192.168.0.3:2081:'REGEX((.*))']"
import org.joda.time.*
import org.openhab.core.library.types.*
import org.openhab.core.library.types.PercentType
import org.openhab.core.library.items.SwitchItem
import org.openhab.model.script.actions.*
import org.openhab.model.script.actions.Timer
import java.lang.*
import java.util.*

var Number cups

rule "Change cups"
when
	Item Coffee_Cups changed
then

var Number cups = Coffee_Cups.state
var String cupsCmd = ""

logInfo("Smarter Coffee", "No of cups:{}", cups)


if(cups == 1){cupsCmd = "\u0036\u0001\u007e\r"}
if(cups == 2){cupsCmd = "\u0036\u0002\u007e\r"}
if(cups == 3){cupsCmd = "\u0036\u0003\u007e\r"}
if(cups == 4){cupsCmd = "\u0036\u0004\u007e\r"}
if(cups == 5){cupsCmd = "\u0036\u0005\u007e\r"}
if(cups == 6){cupsCmd = "\u0036\u0006\u007e\r"}
if(cups == 7){cupsCmd = "\u0036\u0007\u007e\r"}
if(cups == 8){cupsCmd = "\u0036\u0008\u007e\r"}
if(cups == 9){cupsCmd = "\u0036\u0009\u007e\r"}
if(cups == 10){cupsCmd = "\u0036\u000A\u007e\r"}
if(cups == 11){cupsCmd = "\u0036\u000B\u007e\r"}
if(cups == 12){cupsCmd = "\u0036\u000C\u007e\r"}



logInfo("Smarter Coffee", "CupsCmd={}", cupsCmd)

sendCommand(Coffee_CmdText, cupsCmd)
Thread::sleep(1000)

end

rule "Change cups"
when
	Item Coffee_Strength changed
then

var Number strength = Coffee_Strength.state
var String strengthCmd = ""

logInfo("Smarter Coffee", "Strength:{}", strength)

if(strength == 0){strengthCmd= "\u0035\u0000\u007e\r"}
if(strength == 1){strengthCmd = "\u0035\u0001\u007e\r"}
if(strength == 2){strengthCmd = "\u0035\u0002\u007e\r"}


logInfo("Smarter Coffee", "strengthCmd={}", strengthCmd)

sendCommand(Coffee_CmdText, strengthCmd)
Thread::sleep(1000)
end

rule "Brew Coffee"
when
	Item Coffee_Command changed from OFF to ON
then

sendCommand(Coffee_Command, OFF)
logInfo("Smarter Coffee", "Brewing coffee.")

sendCommand(Coffee_CmdText, "\u0037\u007e\r")

end

Thanks for that, I now have a rule working without the need for a list of ifs, hope it helps, I’ve formated a full rule which should work for your coffee machine.

rule "Change cups"
when
	Item Coffee_Cups changed
then
	var int param = (Coffee_Cups.state as DecimalType).intValue
	
	var String paramString = new String(Character::toChars(param))
	var String header = "\u0036"
	var String footer = "\u007e\r"

	var String Cmd = header + paramString + footer;
	sendCommand(Coffee_CmdText,Cmd)
end

ah, sweet. thanks for the link :slight_smile: big coffee fan… soo always looking at ways to automate my coffee habit :slight_smile:

You can check out,

iKettle 2.0 (& Smarter Coffee) protocol figured out so far, and a nice command line app…

It now has a json rest api, command line interface and even a web interface.
Has anybody figured out how to bridge this? Example config file?

It now has a http push and scripting on events… :slight_smile:

I’m trying to integrate iBrew with openHAB2 on a Pi.
I’ve got iBrew compiled and working on the Pi, and can boil my iKettle with the following from the shell:
ibrew legacy heat 10.0.1.4
So, next, looking at the Exec binding (installed) documentation, I’ve added the following to my demo.things file:
exec:command:kettle [command=“ibrew legacy heat 10.0.1.4”, autorun=false]

However as soon as I add that line, my kettle begins boiling!
The command is repeatedly sent, I thought it would only be sent when I trigger the thing.
I’m obviously missing something important, and my understanding of the binding is limited to only it’s documentation page: http://docs.openhab.org/addons/bindings/exec/readme.html

Can anyone give me some pointers before I run up a massive electricity bill?

Figured it out, needed to add “interval=0”

demo.things file:
exec:command:kettle [command=“ibrew legacy heat 10.0.1.4”, interval=0, autorun=false]

demo.items file:
Switch myKettle { channel=“exec:command:kettle:run” }

demo.sitemap file:
Switch item=myKettle label=“iKettle”

Now when I flick the switch, the kettle boils.
I’m sure with some effort, I could add another thing to turn off the kettle:
exec:command:kettlestop [command=“ibrew legacy stop 10.0.1.4”, interval=0, autorun=false]
Then somehow map that to the switch too. That’s beyond me right now though!

Why not a custom binding for ikettle ?

1 Like

I managed to integrate the Smarter Coffee machine with openHAB using the iBrew API. I didn’t use the machine much however so I sold it, but this config might be useful for someone else :slight_smile:

First, clone iBrew from my fork: https://github.com/idserda/iBrew.git (some changes are not (yet) in the main repo)

Then, download additional files (script, icons, maps) here: https://www.dropbox.com/sh/ri93wrny82e0uf7/AABFBzcaR03IuxgvUeuv3dSDa?dl=0

From the command line, execute the following commands. These will add triggers so information is pushed to openHAB. It’s not possible to do this from the ‘console’ mode of iBrew because of the several spaces in the commands. The <script location> is the complete path to the openhab.sh script on the Dropbox link.

./ibrew trigger openHAB switch ON
./ibrew trigger add openHAB HOTPLATE "<script location> SmarterCoffeeHotplate §N" <coffeemaker IP>
./ibrew trigger add openHAB CUPS "<script location> SmarterCoffeeCups §N" <coffeemaker IP>
./ibrew trigger add openHAB STRENGTHTEXT "<script location> SmarterCoffeeStrength §N" <coffeemaker IP>
./ibrew trigger add openHAB GRIND "<script location> SmarterCoffeeGrind §N" <coffeemaker IP>
./ibrew trigger add openHAB WATERLEVEL "<script location> SmarterCoffeeWaterLevel §N" <coffeemaker IP>
./ibrew trigger add openHAB COFFEEBUSY "<script location> SmarterCoffeeBusy §N" <coffeemaker IP>
./ibrew trigger add openHAB COFFEEHEATER "<script location> SmarterCoffeeCoffeeHeater §N" <coffeemaker IP>
./ibrew trigger add openHAB WORKING "<script location> SmarterCoffeeWorking §N" <coffeemaker IP>
./ibrew trigger add openHAB READY "<script location> SmarterCoffeeReady §N" <coffeemaker IP>
./ibrew trigger add openHAB COFFEESTATUS "<script location> SmarterCoffeeStatus '§N'" <coffeemaker IP>
./ibrew trigger add openHAB ENOUGHWATER "<script location> SmarterCoffeeEnoughWater §N" <coffeemaker IP>
./ibrew trigger add openHAB CARAFE "<script location> SmarterCoffeeCarafe §N" <coffeemaker IP>

(example: ./ibrew trigger add openHAB CUPS "/home/jeroen/Applications/iBrew/openhab.sh SmarterCoffeeCups §N" 192.168.1.85)

Then start ibrew with ./iBrew dump events web

I used these items:

Items:

Switch SmarterCoffeeHotplate		"Warmhoudplaat"								<stove>		 	(SaveState)	        {http=">[ON:GET:http://<iBrew ip:host>/api/<coffeemaker IP>/hotplate/on/40] >[OFF:GET:http://<iBrew ip:host>/api/<coffeemaker IP>/hotplate/off]", autoupdate="false"}
Number SmarterCoffeeCups			"Aantal kopjes"								<coffeecups>	(SaveState)			{http=">[*:GET:http://<iBrew ip:host>/api/<coffeemaker IP>/cups/%2$s]", autoupdate="false"}
String SmarterCoffeeStrength		"Sterkte"									<coffeebeans>	(SaveState)			{http=">[*:GET:http://<iBrew ip:host>/api/<coffeemaker IP>/%2$s]", autoupdate="false"}
Switch SmarterCoffeeBrew			"Zet koffie"								<coffeemachine>	(SaveState)			{http=">[ON:GET:http://<iBrew ip:host>/api/<coffeemaker IP>/start] >[OFF:GET:http://<iBrew ip:host>/api/<coffeemaker IP>/stop]", autoupdate="false"}
Switch SmarterCoffeeGrind			"Gebruik bonen"								<coffeebean>	(SaveState)			{http=">[ON:GET:http://<iBrew ip:host>/api/<coffeemaker IP>/beans] >[OFF:GET:http://<iBrew ip:host>/api/<coffeemaker IP>/filter]", autoupdate="false"}
Switch SmarterCoffeeWorking			"Bezig [MAP(aanuit.map):%s]"				<info>			(SaveState)
Switch SmarterCoffeeReady			"Klaar [MAP(aanuit.map):%s]"				<coffee>		(SaveState)
Switch SmarterCoffeeCoffeeHeater	"Verwarmingselement [MAP(aanuit.map):%s]"	<heater>		(SaveState)
Switch SmarterCoffeeBusy			"Bonen malen [MAP(aanuit.map):%s]"			<coffeegrinder> (SaveState)
Number SmarterCoffeeWaterLevel		"Hoeveelheid water [MAP(waterlevel.map):%s]"<measuringcup>  (SaveState)
String SmarterCoffeeStatus			"Status [%s]"								<info>			(SaveState)
Switch SmarterCoffeeEnoughWater		"Genoeg water [MAP(aanuit.map):%s]"			<water>			(SaveState)
Switch SmarterCoffeeCarafe			"Pot geplaatst [MAP(aanuit.map):%s]"		<coffeepot>		(SaveState)

Replace <iBrew ip:host> and <coffeemaker IP>. Example: http://192.168.1.15:2080/api/192.168.1.85/hotplate/off. The state of all items in group SaveState are persisted, you’ll need to configure this yourself.

Sitemap:

Frame label="Smarter Coffee" {
    	Selection item=SmarterCoffeeCups mappings=[ 1="1", 2="2", 3="3", 4="4", 5="5", 6="6", 7="7", 8="8", 9="9", 10="10", 11="11", 12="12" ]
    	Switch item=SmarterCoffeeHotplate
    	Switch item=SmarterCoffeeBrew mappings=[ ON="Start", OFF="Stop" ]
    	Text item=SmarterCoffeeStatus label="Status [MAP(smarterstatus.map):%s]" {
    		Frame label="Instellingen" {
    			Selection item=SmarterCoffeeStrength mappings=[ weak="Mild", medium="Normaal", strong="Sterk"]  visibility=[SmarterCoffeeGrind==ON]
    			Switch item=SmarterCoffeeGrind
    		}
    		Frame label="Status" {
    			Text item=SmarterCoffeeWaterLevel
    			Text item=SmarterCoffeeReady
    			Text item=SmarterCoffeeEnoughWater
    			Text item=SmarterCoffeeCarafe
    		}
    		Frame label="Acties" {
    			Text item=SmarterCoffeeBusy
    			Text item=SmarterCoffeeCoffeeHeater
    		}
    	}
    }

This looks like this:


Let me know if you have any questions :slight_smile:

I’d like to know if Keurig K10 or K15 integrate with OpenHAB? If not, which coffee brewers would you guys recommend?