Anova Sous Vide Cooker Integretaion


(M) #1

Basic Idea

I wanted to add my anova sous vide stick (WiFi Version) status to openhab. Therefor we need to get the information from the (undocumented) API and parse it into openhab.

Later I will add some options to start / stop the stick, set timers etc.

Prerequisites

You need your credentials for the API. I found two ways

  1. Get the information from your rooted android phone (not tested since my phone is not rooted)

https://forum.fhem.de/index.php?topic=56697.0

$ cat /data/data/com.anovaculinary.android/shared_prefs/com.anovaculinary.android_preferences.xml

  1. Get the information via man in the midle attack in your home WiFi

https://github.com/bmedicke/anova.py/blob/master/readme.md


anova.rules

var String anova_cooker = "anova%20fxxxxyxxyxyxx2" 
var String anova_secret = "xxxxxxxx"
			
				
rule "Check Anova Status"
	when 
		Time cron "0 0/1 * * * ?"
	then
		logInfo( "anova.rules" , "Rule triggered")
		var String Anova_ApiCall = "https://api.anovaculinary.com/cookers/%s?secret=%s"
		var Integer minute	= now.getMinuteOfHour
		// Get Info every 5 Minutes or every minute if anova is running to avoid spamming API
		if( (minute%5) == 0 || Anova_Status.state == ON  ){
			logInfo( "anova.rules" , "Requesting API Data")
			var String Anova_Answer = sendHttpGetRequest(String::format(Anova_ApiCall, anova_cooker , anova_secret))
			
			// logInfo( "anova.rules" , Anova_Answer)
			
			// Check JSON answer for Status
			var String Anova_online = transform( "JSONPATH", "$.error.code" , Anova_Answer)
			
			if( Anova_online == "404" ){
				// Anova Offline
				Anova_Online.sendCommand( OFF )
				Anova_Status.sendCommand( OFF )
				Anova_Current_job.postUpdate( "-" )
				Anova_Timer_running.postUpdate( OFF )
				Anova_Timer_remaining.postUpdate( -1 )
				Anova_Target_temp.postUpdate(-1)
				Anova_Current_temp.postUpdate( -1 )
			}else{
				// Anova Online
				Anova_Online.sendCommand( ON )
				
				// Anova temperatures
				Anova_Target_temp.postUpdate( Double::parseDouble( transform( "JSONPATH" , "$.status.target_temp" , Anova_Answer) ))
				Anova_Current_temp.postUpdate( Double::parseDouble( transform( "JSONPATH" , "$.status.current_temp" , Anova_Answer) ))
				
				// Anova status (running or not)
				var String Anova_running = transform( "JSONPATH" , "$.status.is_running", Anova_Answer)
				if( Anova_running == "true" ){
					Anova_Status.sendCommand( ON ) 
					
					// Get job information 
					Anova_Current_job.postUpdate( transform( "JSONPATH" , "$.status.current_job_id", Anova_Answer) )
					
					// Timer
					var String anova_timer = transform( "JSONPATH" , "$.status.is_timer_running", Anova_Answer)
					
					if( anova_timer == "false"){
						// No timer running
						Anova_Timer_running.postUpdate( OFF )
						Anova_Timer_remaining.postUpdate( -1 ) // -1 as default if no timer running
					}else{
						Anova_Timer_running.postUpdate( ON )
						
						var Number anova_timer_remaining =  Integer::parseInt( transform( "JSONPATH" , "$.status.timer_length", Anova_Answer))/60
						Anova_Timer_remaining.postUpdate( anova_timer_remaining )		
					}
				}else{
					// Anova not running
					Anova_Status.sendCommand( OFF )
					Anova_Current_job.postUpdate( "-" )
					Anova_Timer_running.postUpdate( OFF )
					Anova_Timer_remaining.postUpdate( -1 )
				}
			}
		}
end

Anova culinary bluetooth binding