Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ Platform | Description
-- | --
`sensor` | Shows various temperature readings from the grill or accessories
`climate` | Allows temperature control of the grill and probe
`number` | Allows minutes input to the timer
`number` | Allows minutes input to the timer and cook cycles.
`switch` | Allow SuperSmoke, Keepwarm, and connectivity switch
`binary sensor ` | Show values of boolean entities

![device][deviceimg]
![lovelace][lovelaceimg]
Expand Down
2 changes: 1 addition & 1 deletion custom_components/traeger/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):

client = traeger(username, password, hass, session)

await client.start()
await client.start(30)
hass.data[DOMAIN][entry.entry_id] = client

for platform in PLATFORMS:
Expand Down
68 changes: 68 additions & 0 deletions custom_components/traeger/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"""Binary Sensor platform for Traeger."""
from homeassistant.helpers.entity import Entity

from .const import (
DEFAULT_NAME,
DOMAIN,
)

from .entity import TraegerBaseEntity

async def async_setup_entry(hass, entry, async_add_devices):
"""Setup Binary Sensor platform."""
client = hass.data[DOMAIN][entry.entry_id]
grills = client.get_grills()
for grill in grills:
grill_id = grill["thingName"]
async_add_devices([zTimer(client, grill["thingName"], "Cook Timer Complete", "cook_timer_complete")])
async_add_devices([zProbe(client, grill["thingName"], "Probe Alarm Fired", "probe_alarm_fired")])

class TraegerBaseSensor(TraegerBaseEntity):

def __init__(self, client, grill_id, friendly_name, value):
super().__init__(client, grill_id)
self.value = value
self.friendly_name = friendly_name
self.grill_register_callback()

# Generic Properties
@property
def available(self):
"""Reports unavailable when the grill is powered off"""
if self.grill_state is None:
return False
else:
return self.grill_state["connected"]

@property
def name(self):
"""Return the name of the grill"""
if self.grill_details is None:
return f"{self.grill_id} {self.friendly_name}"
name = self.grill_details["friendlyName"]
return f"{name} {self.friendly_name}"

@property
def unique_id(self):
return f"{self.grill_id}_{self.value}"

# Sensor Properties
@property
def state(self):
return self.grill_state[self.value]

class zTimer(TraegerBaseSensor):
"""Traeger Binary class."""

# Generic Properties
@property
def icon(self):
return "mdi:timer"

class zProbe(TraegerBaseSensor):
"""Traeger Binary class."""

# Generic Properties
@property
def icon(self):
return "mdi:thermometer"
3 changes: 2 additions & 1 deletion custom_components/traeger/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
SENSOR = "sensor"
SWITCH = "switch"
NUMBER = "number"
PLATFORMS = [CLIMATE, SENSOR, SWITCH, NUMBER]
BINARY_SENSOR = "binary_sensor"
PLATFORMS = [CLIMATE, SENSOR, SWITCH, NUMBER, BINARY_SENSOR]

# Configuration and options
CONF_ENABLED = "enabled"
Expand Down
160 changes: 160 additions & 0 deletions custom_components/traeger/number.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,179 @@
"""Number/Timer platform for Traeger."""
import voluptuous as vol
from homeassistant.components.number import NumberEntity
from homeassistant.helpers import entity_platform
from homeassistant.helpers import config_validation as cv
import logging

SERVICE_CUSTOMCOOK = "set_custom_cook"
ENTITY_ID = "entity_id"
SCHEMA_CUSTOMCOOK = {vol.Required(ENTITY_ID): cv.string,
vol.Required("steps",default=dict): list}

from .const import (
DOMAIN,
)

_LOGGER: logging.Logger = logging.getLogger(__package__)

from .entity import TraegerBaseEntity

async def async_setup_entry(hass, entry, async_add_devices):
"""Setup Service platform."""
platform = entity_platform.current_platform.get()
platform.async_register_entity_service(SERVICE_CUSTOMCOOK, SCHEMA_CUSTOMCOOK, "set_custom_cook")
"""Setup Number/Timer platform."""
client = hass.data[DOMAIN][entry.entry_id]
grills = client.get_grills()
for grill in grills:
grill_id = grill["thingName"]
async_add_devices([TraegerNumberEntity(client, grill["thingName"], "cook_timer")])
async_add_devices([CookCycNumberEntity(client, grill["thingName"], "cook_cycle", hass)])

class CookCycNumberEntity(NumberEntity, TraegerBaseEntity):
"""Traeger Number/Timer Value class."""

def __init__(self, client, grill_id, devname, hass):
super().__init__(client, grill_id)
self.devname = devname
self.num_value = 0
self.old_num_value = 0
self.cook_cycle = []
self.hass = hass
self.grill_register_callback()

# Generic Properties
@property
def name(self):
"""Return the name of the grill"""
if self.grill_details is None:
return f"{self.grill_id}_{self.devname}"
name = self.grill_details["friendlyName"]
return f"{name} {self.devname.capitalize()}"

@property
def unique_id(self):
return f"{self.grill_id}_{self.devname}"

@property
def icon(self):
return "mdi:chef-hat"

# Value Properties
@property
def value(self):
if self.grill_state is None:
return 0
if self.num_value > len(self.cook_cycle):
_LOGGER.info(f"B.Cook Cycles out of indexes.")
self.num_value = 0
if self.num_value > 0 and not(4 <= self.grill_state["system_status"] <= 6):
_LOGGER.info(f"Steps not available when not cooking. Revert to 0.")
self.num_value = 0
########################################################################
#Scan for next step advance
if self.num_value > 0 and self.num_value == self.old_num_value:
curstep = self.cook_cycle[self.num_value-1]
if "use_timer" in curstep:
if curstep["use_timer"]:
if self.grill_state["cook_timer_complete"]:
self.num_value = self.num_value + 1
elif self.grill_state["probe_alarm_fired"]:
self.num_value = self.num_value + 1
elif "act_temp_adv" in curstep:
if self.grill_state["grill"] > curstep["act_temp_adv"]:
self.num_value = self.num_value + 1
elif "probe_act_temp_adv" in curstep:
if self.grill_state["probe"] > curstep["probe_act_temp_adv"]:
self.num_value = self.num_value + 1
####################################################################
#In step change
if "min_delta" in curstep and "max_grill_delta_temp" in curstep:
if curstep["max_grill_delta_temp"] > self.grill_limits["max_grill_temp"]:
curstep["max_grill_delta_temp"] = self.grill_limits["max_grill_temp"]
if self.grill_state["set"] < curstep["max_grill_delta_temp"]:
if self.grill_state["probe"] > self.grill_state["set"] - curstep["min_delta"]:
set_temp = self.grill_state["set"] + 5
self.hass.async_create_task(self.client.set_temperature(self.grill_id, round(set_temp)))
########################################################################
#Implement next step
if self.num_value > 0 and self.num_value != self.old_num_value: #Only hit once per step.
curstep = self.cook_cycle[self.num_value-1]
if "time_set" in curstep:
self.hass.async_create_task(self.client.set_timer_sec(self.grill_id, round(curstep["time_set"])))
if "probe_set_temp" in curstep:
if curstep["max_grill_delta_temp"] > self.grill_limits["max_grill_temp"]:
curstep["max_grill_delta_temp"] = self.grill_limits["max_grill_temp"]
self.hass.async_create_task(self.client.set_probe_temperature(self.grill_id, round(curstep["probe_set_temp"])))
if "set_temp" in curstep:
self.hass.async_create_task(self.client.set_temperature(self.grill_id, round(curstep["set_temp"])))
if "smoke" in curstep:
if self.grill_state["set"] <= 225 and self.grill_features["super_smoke_enabled"] == 1:
if curstep["smoke"] == 1:
self.hass.async_create_task(self.client.set_switch(self.grill_id, 20))
else:
self.hass.async_create_task(self.client.set_switch(self.grill_id, 21))
if "keepwarm" in curstep:
if curstep["keepwarm"] == 1:
self.hass.async_create_task(self.client.set_switch(self.grill_id, 18))
else:
self.hass.async_create_task(self.client.set_switch(self.grill_id, 19))
if "shutdown" in curstep:
if curstep["shutdown"] == 1:
self.hass.async_create_task(self.client.shutdown_grill(self.grill_id))
self.num_value = 0
self.old_num_value = self.num_value
_LOGGER.debug(f"CookCycle Steps:{self.cook_cycle}")
if self.num_value > len(self.cook_cycle):
_LOGGER.info(f"A.Cook Cycles out of indexes.")
self.num_value = 0
return self.num_value

@property
def min_value(self):
return 0

@property
def max_value(self):
return 999

@property
def state_attributes(self):
"""Return the optional state attributes."""
#default_attributes = super().state_attributes
prev_step = {}
curr_step = {}
next_step = {}
if self.num_value > 1:
prev_step = self.cook_cycle[self.num_value-2]
if self.num_value > 0:
curr_step = self.cook_cycle[self.num_value-1]
if self.num_value < len(self.cook_cycle):
next_step = self.cook_cycle[self.num_value]
custom_attributes = {
"cook_cycl": self.cook_cycle,
"cook_cycl_step": str(self.cook_cycle),
"prev_step": str(prev_step),
"curr_step": str(curr_step),
"next_step": str(next_step),
}
attributes = {}
attributes.update(custom_attributes)
return attributes

# Value Set Method
async def async_set_value(self, value : float):
"""Set new Val and callback to update value above."""
self.num_value = round(value)
#Need to call callback now so that it fires step #1 or commanded step immediatlly.
await self.client.grill_callback(self.grill_id)

# Recieve Custom Cook Command
def set_custom_cook(self, **kwargs):
self.cook_cycle = kwargs["steps"]
_LOGGER.info(f"Traeger: Set Cook Cycle:{self.cook_cycle}")
#Need to call callback now so that it fires state cust atrib update.
self.hass.async_create_task(self.client.grill_callback(self.grill_id))

class TraegerNumberEntity(NumberEntity, TraegerBaseEntity):
"""Traeger Number/Timer Value class."""
Expand Down
68 changes: 68 additions & 0 deletions custom_components/traeger/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@

set_custom_cook:
name: Set Cook Cycle
description: Set Custom Cook Cycle Steps
fields:
entity_id:
description: "Number Entity that serves Custom Cook Steps."
example: "'number.abcdefghi123_cook_cycle'"
steps:
description: |
Steps to perform.
`probe_alarm_fired` will progress the step =OR=
(`cook_timer_complete` AND `use_timer`) =OR=
(`act_temp_adv` < grill temp) =OR=
(`probe_act_temp_adv` < probe temp)

This Service only allows `Steps` Parameter. The other Parameters need to be nested within steps. See example (FILL EXAMPLE DATA), or /config/custom_components/traeger/services.yaml for more info.
required: true
example:
- #1 Set temp and smoke, advance on probe fired, or act_temp_adv
set_temp: 180 #Set grill temp
smoke: 1 #S.Smoke if applicable
act_temp_adv: 170 #Move to next step at this act temp
- #2 PRE-HEAT....Set timer and use timer to advance. #Probe fire would also advance it.
time_set: 900 #Set timer
use_timer: 1 #If set we use the timer to progress if set to zero it won't progress on timer.
- #3 Set Probe Setpoint and Referance timer. Advance on probe_act_temp_adv (since it is lower than probe setpoint Step #2).
# `min_delta` AND `max_grill_delta_temp` can be used to increase grill temp within the step.
probe_set_temp: 205 #Probe Temp
time_set: 64800 #Set timer in SECONDS but no `use_timer` so this is ref. only.
min_delta: 30 #Min Delta that the Grill temp will stay at in relation to probe.
max_grill_delta_temp: 225 #Required for min_delta
probe_act_temp_adv: 160 #adv the step on probe act temp
- #4 Advance on probe_act_temp_adv (since it is lower than probe setpoint Step#2).
# `min_delta` AND `max_grill_delta_temp` can be used to increase grill temp within the step.
min_delta: 40 #Min Delta that the Grill temp will stay at in relation to probe.
max_grill_delta_temp: 250 #Required for min_delta
probe_act_temp_adv: 170 #adv the step on probe act temp
- #5 Advance on `probe_set_temp` (since it is lower than probe_act_temp_adv).
# `min_delta` AND `max_grill_delta_temp` can be used to increase grill temp within the step.
min_delta: 50 #Min Delta that the Grill temp will stay at in relation to probe.
max_grill_delta_temp: 275 #Required for min_delta
probe_act_temp_adv: 206 #Adv if probe registered over setpoint in step 2...safety.
- #6 Set temp to where the probe is @. Set timer. Advance on timer complete
set_temp: 205 #Go down to probe temp
time_set: 900 #Wait 15 minutes for somebody to tell me otherwise
use_timer: 1 #Adv on timer
- #7 Shutdown the grill if the timer is not cancelled.
shutdown: 1 #Nobody aborted...shutdown.
set_temp:
description: "Set Grill Temp. *Only up to your grill's MaxTemp."
smoke:
description: "Set Smoke Mode 1 or 0. *Only Avail if grill supports"
time_set:
description: "Set Timer."
use_timer:
description: "Use Timer to Advance State. *Only applies to current state."
min_delta:
description: "Min DELTA temp between Grill and Probe. *Requires `max_grill_delta_temp`."
max_grill_delta_temp:
description: "Max Temp it will increase to from probe `min_delta`. *Requires `min_delta`."
act_temp_adv:
description: "Grill Temp at which the State will advance."
probe_act_temp_adv:
description: "Probe Temp at which the State will advance."
shutdown:
description: "Call Grill Shutdown."

2 changes: 1 addition & 1 deletion custom_components/traeger/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def is_on(self):
# Switch Methods
async def async_turn_on(self, **kwargs):
"""Set new Switch Val."""
await self.client.start()
await self.client.start(1)

async def async_turn_off(self, **kwargs):
"""Set new Switch Val."""
Expand Down
Loading