Anova sous vide cooker control

This has been a long project with some reverse engineering but it’s finally done! I have created a way for Home Assistant to control my Anova cooker using their undocumented API. My config is based around this, though there is a PR pending for additional functionality: https://github.com/bmedicke/anova.py

Anyway, make sure you have either my version loaded or that the PR is accepted. Once you do that you need the script located here: https://github.com/danodemano/anova.py/blob/master/anova_control.py

Then you need 3 shell commands:

anova_start: '/scripts/anova_control.py -m start -t "{{ states.input_number.anova_temp.state | int }}" -d "{{ states.input_number.anova_duration.state | int }}"'
anova_stop: '/scripts/anova_control.py -m stop'
anova_icebath: '/scripts/anova_control.py -m icebath'

And a CLI sensor:

- platform: command_line
  command: /scripts/anova_control.py -m status -o h
  name: Anova Status
  json_attributes:
    - current_temp
    - is_running
    - is_timer_running
    - speaker_mode
    - target_temp
    - temp_unit
    - timer_length
    - alarm_active
    - job_type
    - job_stage
    - duration
    - job_start_time
  value_template: '{{value_json.current_temp}}'
  scan_interval: 15

And some template sensors:

anova_is_timer_running:
  friendly_name: 'Is Timer Running'
  value_template: '{{ states.sensor.anova_status.attributes.is_timer_running }}'
anova_job_type:
  friendly_name: 'Job Type'
  value_template: '{{ states.sensor.anova_status.attributes.job_type }}'
anova_job_stage:
  friendly_name: 'Job Stage'
  value_template: '{{ states.sensor.anova_status.attributes.job_stage }}'
anova_timer_length:
  friendly_name: 'Cook Time Remaining'
  value_template: '{{ (states.sensor.anova_status.attributes.timer_length/60) | round(0) }}'
  unit_of_measurement: 'min'
anova_job_start_time:
  friendly_name: 'Job Start Time'
  value_template: '{{ as_timestamp(states.sensor.anova_status.attributes.job_start_time) | timestamp_custom("%x %X") }}'
anova_is_running:
  friendly_name: 'Is Running'
  value_template: '{{ states.sensor.anova_status.attributes.is_running }}'
anova_duration:
  friendly_name: 'Cook Duration'
  value_template: '{{ (states.sensor.anova_status.attributes.duration/60) | round(0) }}'
  unit_of_measurement: 'min'
anova_alarm_active:
  friendly_name: 'Alarm Active'
  value_template: '{{ states.sensor.anova_status.attributes.alarm_active }}'
anova_target_temp:
  friendly_name: 'Target Temp'
  value_template: '{{ states.sensor.anova_status.attributes.target_temp }}'
  unit_of_measurement: '°F'
anova_speaker_mode:
  friendly_name: 'Speaker Mode'
  value_template: '{{ states.sensor.anova_status.attributes.speaker_mode }}'
anova_current_temp:
  friendly_name: 'Current Temp'
  value_template: '{{ states.sensor.anova_status.attributes.current_temp }}'
  unit_of_measurement: '°F'
anova_temp_unit:
  friendly_name: 'Temp Unit'
  value_template: '{{ states.sensor.anova_status.attributes.temp_unit }}'

And binary template sensors:

anova_is_timer_running:
  friendly_name: 'Is Timer Running'
  value_template: >-
      {%- if is_state("sensor.anova_is_timer_running", "True") -%}
          True
      {%- elif is_state("sensor.anova_is_timer_running", "False") -%}
          False
      #This accounts for no state if cooker is offline
      {%- else -%}
          False
      {%- endif -%}
  entity_id: sensor.anova_is_timer_running
anova_is_running:
  friendly_name: 'Is Running'
  value_template: >-
      {%- if is_state("sensor.anova_is_running", "True") -%}
          True
      {%- elif is_state("sensor.anova_is_running", "False") -%}
          False
      #This accounts for no state if cooker is offline
      {%- else -%}
          False
      {%- endif -%}
  entity_id: sensor.anova_is_running
anova_speaker_mode:
  friendly_name: 'Speaker Mode'
  value_template: >-
      {%- if is_state("sensor.anova_speaker_mode", "True") -%}
          True
      {%- elif is_state("sensor.anova_speaker_mode", "False") -%}
          False
      #This accounts for no state if cooker is offline
      {%- else -%}
          True
      {%- endif -%}
  entity_id: sensor.anova_speaker_mode
anova_alarm_active:
  friendly_name: 'Alarm Active'
  value_template: >-
      {%- if is_state("sensor.anova_alarm_active", "True") -%}
          True
      {%- elif is_state("sensor.anova_alarm_active", "False") -%}
          False
      #This accounts for no state if cooker is offline
      {%- else -%}
          False
      {%- endif -%}
  entity_id: sensor.anova_alarm_active

I customized the entities and used a custom card but you are left with this:

2019-02-24%2017_48_13-

And expanded:

Then running:

Hope that all makes sense! You will need to get your cooker ID and secret for this to work. Enjoy!!!

11 Likes

Will this work with bluetooth version of Anova ? Can you elaborate on “make sure you have either my version loaded.” I am currently using https://bitbucket.org/pjhardy/anovamaster version on rpi zero w to control my Anova bluetooth. I want to see if the Bluetooth Anova API has icebath function or not.

This is 100% based on the wifi version that communicates via the Internet and Anova’s API. I cannot speak to the Bluetooth version unfortunately. That would require different reverse engineering.

1 Like

This looks great. Is this going to be merged with the main Hass.io build soon?

@Dan_Edwards - unfortunately this is 100% a custom/hack job. While I would love to get it into the core HA code I haven’t the slightest idea how to make that happen. :confused:

I think I see how to get this set up, but just to make sure I do:

  1. download your repo (or pull the version with the PR from https://github.com/bmedicke/anova.py)

  2. Get your cooker id/secret via this method: https://github.com/bmedicke/anova.py/issues/1#issuecomment-446835744

  3. Add the cooker id/secret to the anova_control.py file you added

  4. …???

Here is where I’m unsure where to go. Lots of questions:

  • Where do you store the script/files from Github? in your home directory (ie /home/homeassistant/.homeassistant/scripts?
  • Which files do we actually need? Just anova_control.py and /src/anova.py? With that folder structure too (ie anova.py has to be in /src?).
  • Do we need to run pip install or anything?

I think once I get past those issues I can handle the HASS integration.

Yep on 1/2/3 you are correct. 4 is profit. :smiley:

Anyway, you can put the scripts wherever is convenient for you and the HA user has access to. You only need the anova_control.py. The other one is installed when you do the pip3 install of it.

I think that answers the other two questions as well actually. It’s not a simple process at all TBH, but it does work when all is said and done.

Ha I almost put 4) Profit.

Gotcha. If we changed the location then we’d have to update the shell commands to reflect the correct path.

Compared to the MQTT version I installed and got set up, I think this is much easier. And definitely not “simple” but not difficult once I get the process.

Yeah you can change that location to whatever makes the most sense. I just have one big “scripts” directory that I dump everything into so that went there too.

lol if you say so. I spent 3 weeks working on this but I guess most of that was trying to get the damn cooker id/secret then reverse engineering/testing the ice bath process. If you say it’s simple though I’ll take your word on that. :slight_smile:

Oh, well I didn’t mean to get it working with the API was easy (hats off there, I’d have no clue where to start). I meant setting it up after what you’ve done is easy.

Gotcha. But yeah even the setup of what I have doesn’t seem exactly easy. The MQTT process must really be a pain.

Well it runs on Python 2 so I had to dedicate a pi zero to it and it was…fidgety.

Eww…Python 2. I felt dirty just reading that.

Ha, yep. https://bitbucket.org/pjhardy/anovamaster

Looks like he made a few improvements to it though with timer integration. Might check that out as I already had it all set up.

Looks like I got the hard part accomplished and got things working in HASS. Hardest part was getting that secret/anova_id. Had to bust out my old Nexus 5x and root it to get that info.

Curious how you control things via HASS. Did you create a few input_numbers for the set time and temp?

Yeah, getting that had me hung up for days since the other methods didn’t work. Ended up using a rooted Amazon fire something tablet I had lying around.

Anyway, yeah. Input numbers for the temp/time:

anova_temp:
  name: 'Temperature (°F)'
  min: 60
  max: 200
  step: 1
  mode: box
anova_duration:
  name: 'Cook Duration (min)'
  min: 1
  max: 2160
  step: 1
  mode: box

Then calling the start script uses those numbers when calling the anovy_control.py file.

I think everything can be found in my configs here: https://github.com/danodemano/Home-AssistantConfig

1 Like

The Anova daemon I wrote uses somebody else’s python2 library to talk to the Anova over BTLE. My experience with Bluetooth in python 3 is that a) pyBluez is still annoying to compile, and b) even though the python3 standard library can support Bluetooth sockets, almost no Linux distributions choose to build their packages with that support. The hass.io images definitely don’t, which has crippled my other Bluetooth-based custom component.

I have no idea how BTLE differs from standard Bluetooth in this use case. But I had to choose between finding out, and writing a solution that I knew would let me get push notifications from my sous vide sooner. And I’m doing it without relying on an internet connection and handing over weirdly personal data to a third party.

But thank you for your constructive criticism!

2 Likes

Awesome! Will you get this to an official component?

I don’t think this is really something that should be directly added to HASS. It’s pretty difficult to get the necessary anova_id and secret you need to make it work (the netcat process didn’t work for me at all, had to bust out a rooted phone).

I think MAYBE it could be a custom component, but even then not sure what that gets us. It works now. When i get my setup in HASS I’ll post a complete set of config information (no offense to @danodemano but he’s missing a few things in his first post as I looked through his repo).

1 Like

@phantomdarkness - I agree, it’s not simple at all to get the secret and ID. The netcat process didn’t work for me either, I had to use a rooted Android device. I can’t imagine it would be too hard to make it an official component, but the barrier to entry is still pretty high.

I knew I missed a lot of the UI stuff in the initial post, it was already getting long. I am going to attempt to do up a blog post with a ton more detail as time permits. I actually have a couple of ~3 hour flights coming up that may be the perfect opportunity to at least get the bulk of the writing done. I sorta operated under the assumption that if you jumped through all the hoops to get the communication up with the cooker building out the UI was easy, lol. Perhaps I shouldn’t have done that.