Plugins for smoker control - Big Green Egg, BBQ Guru CyberQ

I have been using a ceramic smoker–specifically, a large Big Green Egg–for around 6 months, and I don’t know how I ever did without it. I’ve got a BBQ Guru CyberQ controller on the way, which will regulate the temperature between ambient & 475 degrees. As an added feature, you can add it to a wireless network and access it from the Internet. I’m curious if anyone has looked into adding this device–as a thermostat, most likely–to Vera with a custom plugin.

I don’t have the programming know-how to do it on my own, but I’m willing to try if there are other interested users. When I get the device, I’m going to start playing with it to see how the commands and status are transmitted to/from the device. It’s all via HTTP, as I understand it. Here are a couple resources:

Product specs
User manual
Arduino forums CyberQ thread

[quote=“DeltaNu1142, post:1, topic:184880”]I have been using a ceramic smoker–specifically, a large Big Green Egg–for around 6 months, and I don’t know how I ever did without it. I’ve got a BBQ Guru CyberQ controller on the way, which will regulate the temperature between ambient & 475 degrees. As an added feature, you can add it to a wireless network and access it from the Internet. I’m curious if anyone has looked into adding this device–as a thermostat, most likely–to Vera with a custom plugin.

I don’t have the programming know-how to do it on my own, but I’m willing to try if there are other interested users. When I get the device, I’m going to start playing with it to see how the commands and status are transmitted to/from the device. It’s all via HTTP, as I understand it. Here are a couple resources:

Product specs
User manual
Arduino forums CyberQ thread[/quote]

I currently use a https://www.rocksbarbque.com/ Stoker to control my Weber Smokey Mountian 22.5"
I’m not sure what advantages I would gain from integrating it into vera.

Here is their software with both Iphone and Andriod Apps but I use the StokerLog first on the list.
https://www.rocksbarbque.com/Applications.html

I’ve just downloaded Pit Pal… that could take care of all of my remote monitoring wants.

There’s no real advantage for Vera integration, other than having everything in one place. The only thing that comes to mind is the ability to shut down (?) automatically in the event of an emergency, if I’m delayed away from home, and so on. Currently, if my home alarm goes off, Vera sends several commands via a PLEG that shuts down AC, etc.

Let me know how you like it. I remember deleting it as there was something i didnt like.

Pit Pal worked incredibly well with my CyberQ. Set point changes were easy. I don’t like having to hit a button to update current temperatures, but overall it works really well. Here’s a brisket I did Sunday. The app logs all changes to settings as you go to give context to the graph, too.

(Tapatalk upload is falling)

Ugh… had to upgrade Tapatalk. Anyway, here’s the graph…

[url=http://tapatalk.imageshack.com/v2/15/01/07/36136cb1fcb3eb0bbfa0591ea06d2d78.jpg]http://tapatalk.imageshack.com/v2/15/01/07/36136cb1fcb3eb0bbfa0591ea06d2d78.jpg[/url]

Thanks!

Heh… just an update. I’m doing my second cook with the CyberQ today, and I noticed a setting for “auto refresh.” You can set it for none, 2 mins, 4 mins, or 8 mins. So really, my only gripe with this device was a result of my failure to RTFM.

I thought I’d revisit this to see if anyone might help me with developing a plugin.

I expect this would operate like a thermostat (for pit temperature) and three temperature sensors (for food probes). I’d want to be able to write temperature setpoints to the device, and read back current temperature for the pit and individual temperatures for the three probes. The temperature setpoints for the food probes only serve to turn on an audible alarm in the controller; there’s no real control of temperature occurring for those setpoints.

The way this web interface works is simple - update temperature setpoints in the interface and press “Submit Values.” There’s an existing iPhone application (PitPal, discussed above) but it comes with a lot of overhead. I’d like just to be able to plug in the device and set the temperatures, without having to set up a whole cook profile each time. Besides, adding the information to Vera has other benefits. I could flash the lights when the food temperature is reached, for example.

A bonus, I suppose, would be the ability to name the probes… but that’s only interesting to me if I can then read them in HomeWave.

Attached to this post is a screen shot of the web interface to the temperature controller.

This is the page source:

<html>
<head>
<style type="text/css">
   *
   {
      font-family: Arial;
   }
   body
   {
      background-color: #FFFFFF;
   }
   table
   {
      border-collapse: collapse;
   }
   table, th, td
   {
      border: 1px solid black;
   }
   th
   {
      background-color: #CCCCCC;
   }
   td
   {
      padding: 5px;
   }
   .top table, .top th, .top td
   {
      border: 0px;
   }
   .noborder table, .noborder th, .noborder td
   {
      border: 0px;
   }
   <!-- statusClassX are the classes used when displaying statusValToStr[] .  statusClass0 is statusValToStr[0], etc.
   -->
   .statusClass0
   {
      color: inherit;
      <!-- OK -->
   }
   .statusClass1
   {
      color: magenta;
      <!-- HIGH -->
   }
   .statusClass2
   {
      color: blue;
      <!-- LOW -->
   }
   .statusClass3
   {
      color: magenta;
      <!-- DONE -->
   }
   .statusClass4
   {
      color: orange;
      <!-- ERROR -->
   }
   .statusClass5
   {
      color: blue;
      <!-- HOLD -->      
   }
   .statusClass6
   {
      color: red;
      <!-- ALARM -->
   } 
   .statusClass7
   {
      color: red;
      <!-- SHUTDOWN -->
   }
   </style>
<script type="text/javascript">
var ajaxGet;
var t;
var waiting = 0;
var tValid = 0;
var ios6workaround = 0;

function initForm()
{
   document.mainForm.COOK_NAME.value = "Big Green Egg";
   document.mainForm.FOOD1_NAME.value = "Spare Ribs";
   document.mainForm.FOOD2_NAME.value = "Salmon";
   document.mainForm.FOOD3_NAME.value = "Food3";
   document.mainForm._COOK_SET.value = TempPICToHTML(2400, 0);
   document.mainForm._FOOD1_SET.value = TempPICToHTML(1600, 0);
   document.mainForm._FOOD2_SET.value = TempPICToHTML(1800, 0);
   document.mainForm._FOOD3_SET.value = TempPICToHTML(1800, 0);
}

function setStartTime()
{
   tValid = 1;
   t = setTimeout('ajax()', 1000);
}

function initScript()
{
   initForm();
   setStartTime();   
}
 
function ajax()
{   
   if (waiting)
   {
      waiting--;
   }
   
   if (!waiting)
   {
      ajaxGet=GetXmlHttpObject();
 
      if (ajaxGet==null)
      {
         alert ("Your browser does not support AJAX!");
         return;
      }

      waiting = 10;  //wait 10 seconds for a response

      ajaxGet.open("POST", "status.xml", true);

      ajaxGet.onreadystatechange = stateChanged;

      ajaxGet.send("IGNOREDTAG="+ios6workaround);
      
      if (++ios6workaround > 255)
         ios6workaround = 0;
   }

   setStartTime();
}

function CookTimerEntryValid()
{
   var ret = 0;
   var input = document.mainForm._COOK_TIMER.value;
   var regex = /^\d{2}:\d{2}:\d{2}$/;
   
   if (input.search(regex) != -1)
   {
      ret = 1;
   }
   
   return ret;
}

function FormAppendHidden(theForm, elementName, elementValue)
{
   //var newElement = document.createElement("<input name='"+elementName+"' type='hidden'>");
   var newElement = document.createElement('input');
   newElement.setAttribute('name', elementName);
   newElement.setAttribute('type', 'hidden');
   
   theForm.appendChild(newElement);
   
   newElement.value = elementValue;
}

function submitForm()
{
   document.mainForm.COOK_SET.value = TempHTMLToPIC(document.mainForm._COOK_SET.value);
   document.mainForm.FOOD1_SET.value = TempHTMLToPIC(document.mainForm._FOOD1_SET.value);
   document.mainForm.FOOD2_SET.value = TempHTMLToPIC(document.mainForm._FOOD2_SET.value);
   document.mainForm.FOOD3_SET.value = TempHTMLToPIC(document.mainForm._FOOD3_SET.value);
   
   //TODO: convert _COOK_TIMER to COOK_TIMER if valid
   if (CookTimerEntryValid())
   {
      FormAppendHidden(document.mainForm, "COOK_TIMER", document.mainForm._COOK_TIMER.value);
   }
   
   document.mainForm.submit();
}

function GetElementAttributeValue(xmlDoc, element, attribute)
{
   var n;
   var i;
   
   n = xmlDoc.getElementsByTagName(element)[0].attributes.length;
   
   for (i=0; i<n; i++)
   {
      if (xmlDoc.getElementsByTagName(element)[0].attributes[i].name == attribute)
      {
         return xmlDoc.getElementsByTagName(element)[0].attributes[i].value;
      }
   }
   
   return "";
}

function GetElementNodeValue(xmlDoc, element)
{
   return xmlDoc.getElementsByTagName(element)[0].childNodes[0].nodeValue;
}

var displayInF=1;

function TempHTMLToPIC(temp)
{
   if (!displayInF)
   {
      temp *= 9;
      temp /= 5;
      temp += 32;
   }
      
   return(Math.round(temp));
}

function TempPICToHTML(temp, agnostic)
{
   if (!displayInF && !agnostic)
   {
      temp -= 320;
      temp *= 5;
      temp /= 9;
   }
   
   temp /= 10;
   
   return Math.round(temp);
}

function DegUnitsChar()
{
   var unit;
   
   if (!displayInF)
   {
      unit = 'C';
   }
   else
   {
      unit = 'F';
   }
   
   return unit;
}

var xmlDirectReplace =
[
   "TIMER_CURR"
];

// needs to go in same order as xmlStatusReplace (except timer_status can be ignored)
var xmlTempReplace =
[
   "COOK_TEMP",
   "FOOD1_TEMP",
   "FOOD2_TEMP",
   "FOOD3_TEMP"
]

var xmlStatusReplace =
[
   "COOK_STATUS",
   "FOOD1_STATUS",
   "FOOD2_STATUS",
   "FOOD3_STATUS",
   "TIMER_STATUS"
];

// see cook_status_t in PIC source
var statusValToStr =
[
   "OK",
   "HIGH",
   "LOW",
   "DONE",
   "ERROR",
   "HOLD",
   "ALARM",
   "SHUTDOWN"
];

function UpdateExistingValues(xmlDoc)
{
   var i;

   if (xmlDoc.getElementsByTagName("OUTPUT_PERCENT")[0] != null)
   {
      document.getElementById("OUTPUT_PERCENT").innerHTML = GetElementNodeValue(xmlDoc, "OUTPUT_PERCENT") + " %";
   }
   
   for (i=0; i<xmlDirectReplace.length; i++)
   {
      var id = xmlDirectReplace[i];
      if (xmlDoc.getElementsByTagName(id)[0] != null)
      {
         if (document.getElementById(id).innerHTML != GetElementNodeValue(xmlDoc, id))
         {
            document.getElementById(id).innerHTML = GetElementNodeValue(xmlDoc, id);
         }
      }
   }

   for (i=0; i<xmlTempReplace.length; i++)
   {
      var id = xmlTempReplace[i];
      if (GetElementNodeValue(xmlDoc, xmlStatusReplace[i]) != 4)
      {
         if (xmlDoc.getElementsByTagName(id)[0] != null)
         {
            document.getElementById(id).innerHTML = TempPICToHTML(GetElementNodeValue(xmlDoc, id), 0) + " " + DegUnitsChar();
            //document.getElementById(id).setAttribute("class", "statusClass0");
            //document.getElementById(id).setAttribute("className", "statusClass0");
         }
      }
      else
      {
         document.getElementById(id).innerHTML = "OPEN";
         //document.getElementById(id).setAttribute("class", "statusClass4");
         //document.getElementById(id).setAttribute("className", "statusClass4");
      }
   }
   
   for (i=0; i<xmlStatusReplace.length; i++)
   {
      var idStatus = xmlStatusReplace[i];
      var idTemp = xmlTempReplace[i];
      
      if ((xmlDoc.getElementsByTagName(idStatus)[0] != null) && (xmlDoc.getElementsByTagName(idTemp)[0] != null))
      {
         document.getElementById(idStatus).innerHTML = statusValToStr[GetElementNodeValue(xmlDoc, idStatus)];
         
         // change class to statusClass0, statusClass1, etc
         document.getElementById(idStatus).setAttribute("class", "statusClass"+GetElementNodeValue(xmlDoc, idStatus));
         document.getElementById(idStatus).setAttribute("className", "statusClass"+GetElementNodeValue(xmlDoc, idStatus));
         document.getElementById(idTemp).setAttribute("class", "statusClass"+GetElementNodeValue(xmlDoc, idStatus));
         document.getElementById(idTemp).setAttribute("className", "statusClass"+GetElementNodeValue(xmlDoc, idStatus));
         
      }
   }
}

function stateChanged()
{
   if (ajaxGet.readyState==4)
   {
      UpdateExistingValues(ajaxGet.responseXML.documentElement);
      
      waiting = 0;
  }
}

function GetXmlHttpObject()
{
   if (window.XMLHttpRequest)
   {
      // code for IE7+, Firefox, Chrome, Opera, Safari
      return new XMLHttpRequest();
   }
   if (window.ActiveXObject)
   {
      // code for IE6, IE5
      return new ActiveXObject("Microsoft.XMLHTTP");
   }
   return null;
}
 </script>
 
 
<title>CyberQ Temperature Controller - Control Status</title>
</head>

<body onload="initScript('');">
<H2>Control Status</H2>

<form name="mainForm" method="POST">
<input type="hidden" name="EEAUTOFLUSH" value="0">

<!--javascript will provide values to this on submit-->
<input type="hidden" name="COOK_SET" value="0">
<input type="hidden" name="FOOD1_SET" value="0">
<input type="hidden" name="FOOD2_SET" value="0">
<input type="hidden" name="FOOD3_SET" value="0">
<P>
<TABLE WIDTH=600>
<TR>
   <TH width=40%>SENSOR</TH>
   <TH width=20%>ACTUAL</TH>
   <TH width=20%>SETPOINT</TH>
   <TH width=20%>STATUS</TH>
</TR>
<TR>
   <TD><input type="text" maxlength=31 name="COOK_NAME"></TD>
   <TD id="COOK_TEMP">&nbsp;</TD>
   <TD><input type="text" maxlength=3 size=4 name="_COOK_SET"> <script>document.write(DegUnitsChar())</script></TD>
   <TD id="COOK_STATUS">&nbsp;</TD>
</TR>
<TR>
   <TD><input type="text" maxlength=31 name="FOOD1_NAME"></TD>
   <TD id="FOOD1_TEMP">&nbsp;</TD>
   <TD><input type="text" maxlength=3 size=4 name="_FOOD1_SET"> <script>document.write(DegUnitsChar())</script></TD>
   <TD id="FOOD1_STATUS">&nbsp;</TD>
</TR>
<TR>
   <TD><input type="text" maxlength=31 name="FOOD2_NAME"></TD>
   <TD id="FOOD2_TEMP">&nbsp;</TD>
   <TD><input type="text" maxlength=3 size=4 name="_FOOD2_SET"> <script>document.write(DegUnitsChar())</script></TD>
   <TD id="FOOD2_STATUS">&nbsp;</TD>
</TR>
<TR>
   <TD><input type="text" maxlength=31 name="FOOD3_NAME"></TD>
   <TD id="FOOD3_TEMP">&nbsp;</TD>
   <TD><input type="text" maxlength=3 size=4 name="_FOOD3_SET"> <script>document.write(DegUnitsChar())</script></TD>
   <TD id="FOOD3_STATUS">&nbsp;</TD>
</TR>
<TR>
   <TD>OUTPUT</TD>
   <TD id="OUTPUT_PERCENT">&nbsp;</TD>
   <TD>&nbsp;</TD>
   <TD>&nbsp;</TD>
</TR>
<TR>
   <TD>TIMER</TD>
   <TD id="TIMER_CURR">&nbsp;</TD>
   <TD><input type="text" maxlength=8 size=9 name="_COOK_TIMER"></TD>
   <TD id="TIMER_STATUS">&nbsp;</TD>
</TR>
</TABLE>
</P>
<input type="hidden" name="EEAUTOFLUSH" value="1">
<P><div class="noborder">
<p><TABLE BORDER=0><TR><TD><input type="button" value="Submit Values" onclick="submitForm()"/></TD><TD><input value="Cancel Changes" type="button" onclick="initForm();"/></TD></form><form action="reboot.htm"><TD><input type="submit" value="Reboot Device"></TD></form></TABLE></P>
</div></P>
<P><div class="top">
  <table border="0">
    <tr>
      <td>
        <A HREF="/"><img src="logo.jpg" align="left" width="125" height="150" border=0/></A>
      </td>
      <td valign="bottom">
        <H1>CyberQ Temperature Controller</H1>
        <a href="/">Main Screen</A> | <a href="system.htm">System Setup</A> | <a href="control.htm">Control Setup</A> | <a href="wifi.htm">WIFI Setup</A> | <a href="email.htm">Email Alerts</A>
      </td>
    </tr>
  </table>
</div></P>
</body>
</html>

Hey Delta,

I’m looking at the CyberQ for my Pit Barrel Cooker to get some more control over the temps.

Are you still using it, and did you get it integrated with Vera?

Thanks

Nope, I never really followed up with it. I have since sold my BGE because we moved into an apartment temporarily, but I kept the controller in anticipation of one day getting another one.so, I’m still interested in a solution.

Sent from my iPhone using Tapatalk