Package: haco
hacoHome Assistant Control Objectshacois a Home Assistant Add-On, and associated Tasmota daemon written in Berry, to greatly simplify the process of
exposing Home
Assistant controls (e.g. Pull-down Lists, Number Sliders, Sensors, etc.) from a Tasmota device - and handling the
communication between both sides.Usinghacoto expose, for example, a "Restart Tasmota" button to Home Assistant is as simple as writing the following
Python code:fromhacoimportButton,Tasmotarestart=Button('Restart Tasmota',icon='restart')@restart.callback()defha(value)->Tasmota[str,'tasmota.cmd("restart 1")']:returnvalueA fully functional restart button will now appear associated with the device in Home Assistant:Or, more practically, a cookbook pull-down menu, for a Tuya air fryer, might look like this:OPTIONS=['Default','Fries','Shrimp','Pizza','Chicken','Fish','Steak','Cake','Bacon','Preheat','Custom']cookbook=Select('Cookbook',options=OPTIONS,icon='chef-hat')@cookbook.callback()defha(value:str)->Tasmota[int,tuya.send(4,3)]:returnOPTIONS.index(value)@cookbook.callback(trigger=tuya.received(4,3))deftasmota(value:int):returnOPTIONS[value]Do I Need This? Can't I Do this with Native Tasmota?You certainly can.But in my experience, the process is so fiddly, error-prone and hard to maintain that it's enough to
deter the casual user (as I am) entirely. Plus, sharing your configuration, once you've finally got it working, can mean
complex step-by-step guides, setting up triggers, finding MAC addresses and topics (in Tasmota) - and numerous
Blueprints, Helpers and Templates (on the Home Assistant side).You can see how much work creating such guides involves by seeing how it
washeroically undertaken by Blakadder, as
compared with
thefullhaco-based equivalent.Withhaco, on the other hand, the thorny parts of the initial setup are abstracted away and your final configuration
can often be shared via a single script. Below is a list of some of the tasks thathacohandles for you:Announcing the entity via MQTT to Home AssistantGenerating MQTT/HA-friendly unique IDsAssociating the entity with its parent deviceSubscribing and publishing to the relevant MQTT topicsManaging the relevant Tasmota rulesAppropriate serialization of dataTranslating Home Assistant messages to their to appropriate Berry data types, and vice versaPre-Release:warning: This library is currently in a pre-release state. The configuration format is likely to change, and
onlySensor,Select,Button,Number,BinarySensor,Switch,Text,Update,FanandClimateentities are currently implemented.InstallationInstallation involves installing two parts: the Tasmota daemon and the Home Assistant Add-On. Both are fairly simple.TasmotaI'd advise first using a guinea pig Tasmota device to gethacoup and running. The device needs to have MQTT
connected and be available to Home Assistant (i.e. showing up
inthe Devices section).Once your test device is ready, with a recent (12.5+) version of Tasmota, simply paste the following into your Tasmota
Berry Script Console:{"download":tasmota.urlfetch('https://link.frontmatter.ai/haco/tapp','haco.tapp'),"restart":tasmota.cmd("restart 1")}You should see output like{'restart': {'Restart': 'Restarting'}, 'download': 200}and the device will restart.Once the device restarts, you should see the following among your Tasmota startup logs (in the regular console, not the
Berry one):HACO: The haco daemon has started. Hostname: tasmota-test, MAC: 04:74:77:9B:CB:CC. Listening for configuration...Older Tasmota VersionsWith older Tasmota versions, download thehaco.tappfileon a desktop
computer,
upload it to your device and restart manually.Home AssistantYou can now install the Home Assistant Add-On:Once the Add-On is installed, click into its Configuration tab and you'll see a setting calledassignments:-config:nullidentifier:nullAssigning a configuration module to a deviceNow we're ready to assign a configuration module to our Tasmota device. For now, we can use a built-in test
moduletest. Theidentifervalue should be the hostname (or MAC) of your Tasmota device. So for our example it
should look like this:-config:testidentifier:tasmota-test# Hostname of Tasmota deviceSaving the Configuration will restart the Add-On. After the restart, you should see the following in the Add-On logs:Device "Tasmota Test Device" successfully configured. Hostname: tasmota-test MAC: 04:74:77:9B:CB:CC.In Home Assistant, find the device inthe Devices section. You should
now see a test Select (pulldown) control called "Greeter".Selecting the greetings from the pulldown, you should see them appear in your Tasmota Berry console, e.g.haco says: Hello World!This meanshacois set up and working properly. Good! You can now usehacoto assign configuration modules to your
Tasmota devices.Configuration ModulesConfiguration modules are a little like drivers, that tellhacohow to communicate with a Tasmota device, what
controls to expose to Home Assistant, etc.So far we've only used thetestconfiguration module, that exposed the Greeter pulldown, but to control
real-world devices, you'll need to assign them to a module written specifically for that device. There are currently two
types of modules,
Built-In and User-Defined.Whichever type you use, assigning them to a device is done as we saw above.Built-In Moduleshacoships with a small number of built-in configurations. These can be found in
theconfigs directoryin this repo. But in the likely event
that your device hasn't already
been added, you'll need to define your own.:information_source: The aim is to expand the number of built-in configurations over time. So if you define your own (
see below), please submit it to this repo as a Pull Request, or raise an Issue linking to your code!Creating Your Own Configuration ModuleGeneral module structureA configuration module is, ultimately, just a typical Python module. In this case, they contain definitions from one or
moreControlobjects. Each control definition involves two things:The control object assigned to a variable, e.g.temperature_sensor=Sensor(...)Any required callback functions for the control, e.g.@temperature_sensor.callback(...)<function>LocationUser-Defined modules should be added to the/config/hacodirectory in Home Assistant, with a.pyextension,
e.g./config/haco/my_config.py.Defining controlsHere are a couple of simple examples to help get you started defining your own configuration module.Tasmota-Only ControlsFirst, let's define a Tasmota-only sensor to report how much free memory there is on the Tasmota device. This sensor
is "Tasmota-Only" as it only handles values sentfromTasmota (and sends nothing back from Home Assistant).fromhacoimportSensormemory=Sensor('Memory Free',icon='memory',uom=Sensor.UOM.DATA_KILOBYTES,uom_type=Sensor.UOM_TYPE.DATA_SIZE)@memory.callback(trigger='Tele#Heap')deftasmota(value:int):returnvalueLet's break this control definition down into the control object, the callback decorator, and the callback itself.The control objectFirst we initialise aSensorcontrol object, assign it to a variable calledmemory, give it a relevant icon, unit of
measure and type.The callback decoratorThe callback decorator@memory.callback(trigger='Tele#Heap')specifies a Tasmota trigger, the values from which will
be sent to the callback function.
This specific trigger,Tele#Heap, fires whenever telemetry data is returned from Tasmota and, specifically, returns
theHeapfield, which contains the free
memory in kilobytes.:warning: Tasmota callbacksmusthave such atriggerspecified in this decorator.The callbackNote that the callback function is namedtasmota. This name is important as it tellshacothat this callback
handles valuesfromTasmota.The response from the trigger we specified in our callback decorator above gets fed into our callback as thevalueargument. The (optional) type annotation for thevalueargument (theint) tellshacowhat data type to expect from
Tasmota, and type conversion gets done automatically.The return value from the function is what will be returned to Home Assistant as the value for the sensor.:information_source: Note that our callback function does not actuallydoanything with ourvalueother than return
it. But if we wanted to (for example) convert the value from kilobytes to bytes, this is where we'd do it,
withreturn value*1000.:information_source: You can find the list of available units of measurehereand
typeshere.Home Assistant-Only ControlsMoving on to the opposite type of control, that is one that only handles valuesfromHome Assistant. The Button
control does
exactly this, as it is triggered by being pressing in Home Assistant, but Tasmota returns nothing to acknowledge that
press.Here is how to define aButtoncontrol that, when pressed, restarts the Tasmota device.fromhacoimportButton,Tasmotarestart=Button('Restart Tasmota',icon='restart')@restart.callback()defha(value)->Tasmota[str,'tasmota.cmd("restart 1")']:returnvalueIn the case we define aButtoncontrol, and again use our decorator to add a callback to handle valuesfromHome
Assistant, which must therefore be namedha.TheTasmotaType AnnotationThe most notable difference with a Home Assistant (ha) callback is theTasmotareturn type annotation. This takes
two arguments,Tasmota[<type>,<berry_expression>]. The
first argument is the type returned by the callback function, as per usual, and which again letshacoconvert the
return value accordingly.The second argument is an expression that defineswhat to doon the Tasmota side when the button is pressed. In this
case, this
is just a Berry expression to restart Tasmota.:warning: Note that Home Assistant callbacksmusthave such aTasmotatype annotation for their return type.:information_source: In the example above, thevalueargument received and returned by our callback is not actually
used by the Berry expression side. Home Assistant just sends a constant string (PRESS) when the button is pressed, so
the actual content of
thevalueargument is not important.Two-Way ControlsTwo-way controls need to handle values frombothHome Assistant and from Tasmota, and hence need both the callbacks
types -haandtasmotawe've seen above.Here is an example two-waySelect(pulldown menu) object. It allows setting the TasmotaWebLogconsole debugging
level from Home Assistant, and also reflects any changes to that setting made on the Tasmota side.fromhacoimportSelect,TasmotaLOG_LEVELS=["None","Error","Info","Debug","Debug More"]weblog=Select('WebLog Level',icon='console',options=LOG_LEVELS)@weblog.callback()defha(value)->Tasmota[str,'tasmota.cmd("WebLog "+value)']:returnLOG_LEVELS.index(value)@weblog.callback(trigger='WebLog')deftasmota(value:int):returnLOG_LEVELS[value]The above is very similar to what we have already seen.Thehacallback takes friendly names (e.g.Error,Info) selected by the user in Home Assistant, and returns them
as their corresponding number values (WebLog 1,WebLog2) used by Tasmota.Thetasmotacallback, naturally enough, does the exact opposite, returning the string names to Home Assistant whenever
theWebLogcommand is used on the Tasmota side.Debugging Your ConfigurationWhen you first assign a new configuration to a device, keep an eye on the Add-On logs for any errors. You'll see a
typical Python stack trace if anything goes wrong.:warning: Each time you edit your configuration module (.pyfile), you'll need to restart the Add-On. Hot
is reloading not yet supported.Additional CapabilitiesMore complex device controls (e.g.Climate,Fan) have additional capabilities beyond a single value requiting
Tasmota/Home Assistant
callbacks. Adding callbacks for these extended capabilities is done similarly to the above, only the callbacks need
to include the name of the capability. So if you want to add callbacks around setting thetarget_humidityof
aClimatecontrol, you'd need to call your callbackstarget_humidity_tasmotaandtarget_humidity_ha, etc.A full schema of these names will appear in subsequent documentation. But you can find example implementations for most
controls in thebuilt-indevelopmentconfig.Anway, if you get the names wrong, you'll see an error like this in your Add-On logs:ValueError: Callback function name "set_mode_ha" is not valid for control "Climate". Must be one of: ['action_tasmota', 'current_humidity_tasmota', 'current_temperature_tasmota', 'mode_ha', 'mode_tasmota', 'preset_mode_ha', 'preset_mode_tasmota', 'swing_mode_ha', 'swing_mode_tasmota', 'target_humidity_ha', 'target_humidity_tasmota', 'target_temperature_ha', 'target_temperature_tasmota', 'temperature_high_ha', 'temperature_high_tasmota', 'temperature_low_ha', 'temperature_low_tasmota']Additional ResourcesBeyond these examples, when creating your own configuration, you'll probably find
thebuilt-in configurations(especially
thedevelopmentconfig) useful.