I recently acquired a Generac brand standby generator. It’s a 22KW Reliance (rebadged Guardian series) generator system, air-cooled and runs off of propane (in my case, or natural gas as an alternative). Of course, I had to instrument it to figure out what it was up to, and minimally I wanted to know if I was in the middle of a power outage, running on generator power.
Generac offers a cloud-based accessory that uses a cellular modem (and just in the last month or two, a wi-fi based version) that can provide simple outage information. Of course there’s a monthly fee for this, and that solution isn’t really all that great in my case… I don’t have reliable cell coverage at my home, and my cable ISP will often go out during power failures since they don’t have backup power for the trunk amplifiers in their outside plant. So no way to report the outage to the cloud, and now way to get an outage notification back from the cloud into Home Assistant.
If you poke around, you’ll find this open-source project called “genmon” which is a series of python scripts that runs on a raspberry pi and talks to the generator via the same accessory port that the Generac cellular modem accessory uses; turns out it speaks ModBus. See GitHub - jgyates/genmon: Generac (and other models) Generator Monitoring using a Raspberry Pi and WiFi for this very cool bit of software! If you have one of these generators, even if you don’t want to connect to Home Assistant, this genmon package will give you way more visibility into the state and performance of your generator, including Alarm conditions, maintenance warnings, current state, load, RPM, utility and generator voltage and much more. You probably want genmon for your Generac generator!
I started thinking about how to hack on this thing to get it to talk to Home Assistant; maybe some MQTT client bashed into genmon, or some custom component ? Turns out that genmon has some interfaces that, in addition to it’s own pretty UI, can return JSON responses with all the good stuff…
So all I needed to do was cobble up some REST and template sensors to extract the information needed; no new code required.
This is still very early days, and has not been tested “in anger” during an actual power outage. But it might serve as a useful example in the mean time.
Why do this? Mostly because I can… and also to provide some notification that utility power is lost and we’re running on backup generator power. Unless you can hear the generator running, you don’t actually know this is the case - so maybe now’s not the time to run the Electric Clothes Dryer, Electric Ovens, all while the AC is cooling things down… the generator actually has the capacity to do so, but I think the fuel costs are about $5 an hour at the elevated loads…
Enjoy. I’m sure there’s plenty of room for improvement and I’ve not really started on any useful automations yet. This is structured to be dropped into the “packages” directory and self-contained.
(Edit 28 May 2021) Here’s the most recent version of my Home Assistant configuration. It no longer uses the REST-base approach described above.
(Edit 22 Dec 2022) Updated with new-style MQTT sensor configuration. Also see this post in the thread which is a variation that assumes that “JSON for Numerics” is turned on in genmon for a different variation, and without my my funky 19916/
MQTT topic prefix.
generator.yaml
################################################################
## Package / Generator
################################################################
#
# Version 0.6
#
# (This file is used as a "package" within the Home Assistant
# configuration files. If you don't use packages with your Home
# Assistant configuration files, you need only take the relevant
# sections of this file and add them to where you define
# sensors, binary_sensors, etc.)
# This package is used to interwork with (only) Generac-brand standby
# generator systems with Nexus, Evolution and H100 controllers, also
# using the open-source "genmon" package. This package typically
# runs on a dedicated Raspberry Pi (2, 3 or Zero) and is connected
# via a serial cable to the controller located inside the generator
# cabinet. See:
# https://github.com/jgyates/genmon
# for additional information about genmon, how it works and what's
# required to implement it. Thanks very much to the author for a
# quite complete and featureful monitoring system that can send
# email, SMS notifications and many other things!
# The genmon package consists of a number of components, the primary
# one being the actual Python script that communicates with the
# generator using a ModBus protocol.
# In practice, this means running the genmon.py and genserv.py (web
# server) daemons on the Raspberry Pi connected to the generator.
# Among the optional components is a daemon which can monitor
# the generator via genmon, and publish relevant information to
# MQTT topics. You can enable this MQTT genmon component using
# the genmon web UI. Of note: you'll need to ensure that the
# MQTT topics match what's configured as topics in this file.
# This module implements a means to collect this data without
# requiring a custom component, or additional software that needs to
# run. This uses a number of MQTT sensors and jinga2 templates to
# receive the published data and expose them as sensor entities in
# Home Assistant.
# Note that the MQTT topics in this file all have a "19916/" prefix
# specified on the otherwise default MQTT topics that genmon would
# use. The only reason this is present is to match the local
# convention that I happen to use personally, and ought not to be
# considered any kind of recommendation. Likely, you'll want to just
# remove that prefix when adapting this configuration for your own
# purposes.
# genmon MQTT flushes out MQTT updated every 60 seconds. You can choose
# to have MQTT sensors for any or all of these data. Note that some
# of the published data changes rather often, such as wifi RSSI,
# uptime counters, etc. You might want to configure your recorder
# component to not store those values to avoid needlessly bloating
# the recorder database with historically uninteresting data.
# The MQTT configuration presumes that you have the "JSON Numerics"
# configuration switch turned OFF in the genmon MQTT configuration.
# If you need this enabled for other reasons, then you'll need to
# select an alternative topic with the "/value" suffix appended for
# a number of the MQTT sensors that are defined. These are present
# below in the configure, but commented out. You'll need to
# manually switch the topics in this circumstance.
# note that the unique_id values in this file are not signficant (or
# even required at all). They exist so that the entities show up
# within the Home Assistant configuration GUI for customization. I
# chose to use UUIDs because it was an easy source of unique values,
# no harm would likely come from using these same values or any that
# you choose from on your own.
# Louis Mamakos <[email protected]>
# 28 May 2021
################################################
## Customize
################################################
homeassistant:
customize:
################################################
## Node Anchors
################################################
package.node_anchors:
customize: &customize
package: 'generator'
################################################
## generator customization
################################################
binary_sensor.genmon_power_outage:
friendly_name: 'Generator Power Outage'
sensor.genmon_outage_status:
friendly_name: 'Generator Status'
sensor.genmon_outage_utility_voltage:
friendly_name: 'Generator Utility Voltage'
sensor.genmon_generator_battery:
friendly_name: 'Generator Battery Voltage'
sensor.genmon_generator_rpm:
friendly_name: 'Generator RPM'
sensor.genmon_generator_frequency:
friendly_name: 'Generator Frequency'
sensor.genmon_generator_output_voltage:
friendly_name: 'Generator Output Voltage'
sensor.genmon_generator_output_current:
friendly_name: 'Generator Output Current'
sensor.genmon_generator_output_power:
friendly_name: 'Generator Output Power'
sensor.genmon_generator_last_alarm_log:
friendly_name: 'Generator Last Alarm'
sensor.genmon_generator_last_service_log:
friendly_name: 'Generator Last Service Log'
sensor.genmon_generator_last_action:
friendly_name: 'Generator Last Action'
sensor.genmon_generator_capacity:
friendly_name: 'Generator Capacity'
sensor.genmon_generator_exercise_time:
friendly_name: 'Generator Exercise Time'
sensor.genmon_mon_cpu_temperature:
friendly_name: 'Generator CPU Temperature'
sensor.genmon_mon_uptime:
friendly_name: 'Generator Monitor Uptime'
sensor.genmon_mon_load:
friendly_name: 'Generator Monitor Load'
sensor.genmon_outage_summary:
friendly_name: 'Generator Outage Summary'
################################################
## generator sensors
################################################
#
# The MQTT topics need to match what's transmitted by the
# genmon host. I use a non-standard MQTT prefix of '19916/' to
# conform with the convention that I happen to use with my own
# home automation system.
#
sensor:
- platform: filter
unique_id: "f0231fdf-7f1c-4acf-ad8d-c4245b722321"
name: Genmon Generator Battery
entity_id: sensor.genmon_generator_battery_raw
filters:
- filter: time_throttle
window_size: "00:15"
- platform: filter
unique_id: "25046a19-4cf0-45d2-8834-cedde8b6a3dc"
name: Genmon Mon CPU Temperature
entity_id: sensor.genmon_mon_cpu_temperature_raw
filters:
- filter: time_throttle
window_size: "00:10"
mqtt:
sensor:
- name: Genmon Outage Status
unique_id: "fd8f8c07-7d6f-4aea-92b5-16b7d8acc66b"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
state_topic: '19916/generator/Outage/Status'
- name: Genmon Outage Utility Voltage
unique_id: "f565fed6-8a19-49d4-8066-4015c5ca91b1"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
state_topic: '19916/generator/Outage/Utility Voltage'
# state_topic: '19916/generator/Outage/Utility Voltage/value' # if JSON numerics
value_template: '{{ value | regex_replace(" V", "") | float | round(0) }}'
unit_of_measurement: "VAC"
# status related sensors
- name: Genmon Generator Battery Raw
unique_id: "713eb4b9-feec-4425-8c83-84a9f486aae7"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
state_topic: '19916/generator/Status/Engine/Battery Voltage'
# state_topic: '19916/generator/Status/Engine/Battery Voltage/value' # if JSON numerics
unit_of_measurement: "V"
value_template: '{{ value | regex_replace(" V", "") | float | round(1) }}'
- name: Genmon Generator RPM
unique_id: "f3a8aedd-206b-4e5f-baa9-677496ee8d3c"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
unit_of_measurement: "RPM"
state_topic: '19916/generator/Status/Engine/RPM'
# state_topic: '19916/generator/Status/Engine/RPM/value' # if JSON numerics
value_template: '{{ value | int }}'
- name: Genmon Generator Frequency
unique_id: "cbdc9c28-5748-4aef-91bc-5b5ec95ed545"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
unit_of_measurement: "HZ"
state_topic: '19916/generator/Status/Engine/Frequency'
# state_topic: '19916/generator/Status/Engine/Frequency/value' # if JSON numerics
value_template: '{{ value | regex_replace(" Hz","") | float }}'
- name: Genmon Generator Output Voltage
unique_id: "f5f462db-e16d-4596-9bd3-232b70fa5038"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
unit_of_measurement: "VAC"
state_topic: '19916/generator/Status/Engine/Output Voltage'
# state_topic: '19916/generator/Status/Engine/Output Voltage/value' # if JSON numerics
value_template: '{{ value | regex_replace(" V","") | float | round(1) }}'
# current measurement is garbage on 22KW product, apparently
- name: Genmon Generator Output Current
unique_id: "10e1def6-35fd-4922-932a-90f25abb7f29"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
unit_of_measurement: "A"
state_topic: '19916/generator/Status/Engine/Output Current'
# state_topic: '19916/generator/Status/Engine/Output Current/value' # if JSON numerics
value_template: '{{ value | regex_replace(" A","") | float | round(1) }}'
# current measurement is garbage on 22KW product, apparently, so this value may
# also be suspect on that model
- name: Genmon Generator Output Power
unique_id: "914a4e97-54a0-41e7-bbeb-a256134e5e35"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
unit_of_measurement: "W"
state_topic: '19916/generator/Status/Engine/Output Power (Single Phase)'
# state_topic: '19916/generator/Status/Engine/Output Power (Single Phase)/value' # if JSON numerics
# this is normalized to watts from kilowatts
value_template: '{{ value | regex_replace(" kW","") | float * 1000 | round(0) }}'
- name: Genmon Generator Last Alarm Log
unique_id: "8cb64471-9123-48f6-b9be-15c01595981e"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
state_topic: '19916/generator/Status/Last Log Entries/Logs/Alarm Log'
- name: Genmon Generator Last Service Log
unique_id: "bce3182a-a525-47dc-937c-8be4e5afc619"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
state_topic: '19916/generator/Status/Last Log Entries/Logs/Service Log'
- name: Genmon Generator Last Action
unique_id: "bf48e972-0b96-489a-9eee-4f2030017c3f"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
state_topic: '19916/generator/Status/Last Log Entries/Logs/Start Stop Log'
# maint related sensors
- name: Genmon Generator Capacity
unique_id: "bf7cbe92-2b1e-48ca-addd-df963697626c"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
state_topic: '19916/generator/Maintenance/Rated kW'
# normalize to watts
value_template: '{{ value | regex_replace(" *[Kk]W", "") | float * 1000 | round(1) }}'
unit_of_measurement: 'W'
- name: Genmon Generator Exercise Time
unique_id: "825657b6-c67a-427a-bee9-7b6ea2182628"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
state_topic: '19916/generator/Maintenance/Exercise/Exercise Time'
# might be nice to maybe somehow normalize this, but not sure into what date/time representation
# value is something like: "Weekly Sunday 17:35 Quiet Mode On"
- name: Genmon Total Run Time
unique_id: "41c981db-5979-4ee6-b9fb-d1ca44f83b3f"
expire_after: 300
state_topic: '19916/generator/Maintenance/Service/Total Run Hours'
value_template: '{{ value | regex_replace(" +h$", "") | default(0) | float }}'
unit_of_measurement: hours
- name: Genmon Hardware Version
unique_id: "927b24b7-772d-434a-8fa1-4f1c5549c90d"
expire_after: 300
state_topic: '19916/generator/Maintenance/Service/Hardware Version'
- name: Genmon Firmware Version
unique_id: "4cf182de-c37b-48c9-8727-75e1f5053d0d"
expire_after: 300
state_topic: '19916/generator/Maintenance/Service/Firmware Version'
# genmon monitor related sensors
- name: Genmon Mon CPU Temperature Raw
unique_id: "792c8096-84a3-4870-ac23-8cc5dcc8f8f0"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
unit_of_measurement: "°F"
device_class: temperature
state_topic: '19916/generator/Monitor/Platform Stats/CPU Temperature'
value_template: '{{ value | regex_replace(" F","") | float | round(0) }}'
- name: Genmon Mon Uptime
unique_id: "7c398cb0-43bf-469b-b49f-ae8b526cac32"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
# maybe parse this out into minutes/hours?
state_topic: '19916/generator/Monitor/Platform Stats/System Uptime'
- name: Genmon Mon Load
unique_id: "60b44a17-2535-47da-af10-7f6b45d35d3a"
expire_after: 300 # genmon MQTT flushes out MQTT updated every 60 seconds
unit_of_measurement: '%'
state_topic: '19916/generator/Monitor/Platform Stats/CPU Utilization'
value_template: '{{ value | regex_replace(" *%","") | float | round(1) }}'
- name: Genmon Version
unique_id: "9bc1d83a-34bc-4a1e-a643-3c0eb62501dd"
expire_after: 300
state_topic: '19916/generator/Monitor/Generator Monitor Stats/Generator Monitor Version'
# note that these following are unique to my own specific situation and
# are values collected from 1-wire bus temperature sensors connected to the Raspberry
# Pi running genmon. These do not come from the standard generator controller or
# the on-board temperature sensor in the Raspberry Pi ARM CPU. You should omit
# these sensors if you're adapting my file for your own purposes.
- name: Genmon 1W Controller Box
unique_id: "882cb971-bf3e-4281-a158-bf0d0e3eb4ea"
expire_after: 600
unit_of_measurement: °F
state_topic: '19916/generator/Status/External Temperature Sensors/28-00000bc56fa5'
value_template: '{{ value | regex_replace(" *F", "") | float | round(1) }}'
- name: Genmon 1W Outside Air
unique_id: "1f54deef-b168-431a-aa9d-2af8e1fbf2c7"
expire_after: 600
unit_of_measurement: °F
state_topic: '19916/generator/Status/External Temperature Sensors/28-020a91770670'
value_template: '{{ value | regex_replace(" *F", "") | float | round(1) }}'
- name: Genmon TC Generator Body
unique_id: "8ca45d5e-91fb-4826-bc75-ea1b3d8046c4"
expire_after: 600
unit_of_measurement: °F
state_topic: '19916/generator/Status/External Temperature Sensors/3b-6d800cc77f01'
value_template: '{{ value | regex_replace(" *F", "") | float | round(1) }}'
- name: Genmon TC Oil Cooler
unique_id: "545c8624-4428-4791-ba95-df671b74aafe"
expire_after: 600
unit_of_measurement: °F
state_topic: '19916/generator/Status/External Temperature Sensors/3b-4c880b6ac367'
value_template: '{{ value | regex_replace(" *F", "") | float | round(1) }}'
- name: Genmon TC Engine Body
unique_id: "d6f20e32-04bf-4155-842f-d68346e3a68b"
expire_after: 600
unit_of_measurement: °F
state_topic: '19916/generator/Status/External Temperature Sensors/3b-4c880b6ac389'
value_template: '{{ value | regex_replace(" *F", "") | float | round(1) }}'
binary_sensor:
#
# probably the most useful sensor - are we in a power outage condition or not?
#
- name: Genmon Power Outage
unique_id: "468414df-32b1-4127-8636-4202a11b880c"
state_topic: '19916/generator/Outage/System In Outage'
payload_off: 'No'
template:
- sensor:
# This particular sensor is probably a bad idea because it continuously updates
# its value during an outage. The convention in Home Assistant these days
# seems to be to record a timestamp reflecting the onset of some condition,
# and then use some sort of clever UI presentation to display the interval
# or elapsed time. The problem here is that this will spam the recorder database
# (and influxdb, if you're using that component) with constantly changing values
# while an outage is in progress. In practice, this only happens during outages
# while hopefully are not that frequent, so the "damage" done to these databases
# is, in practice, minimal. Still, this isn't a great example of how to do things,
# though does illustrate some jinja2 template acrobatics.
- name: generator_outage_summary
unique_id: "11be76cc-02bc-4868-b033-5efc60a289c5"
state: >-
{% if states.sensor.genmon_outage_status.state | regex_match('System in outage') %}
{% set duration = (as_timestamp(now()) - as_timestamp(states.sensor.genmon_outage_status.state)) | regex_replace('System in outage since ') | int %}
{% set hours = duration // 3600 %}
{% set duration = duration - hours * 3600 %}
{% set minutes = duration // 60 %}
{% set seconds = duration % 60 %}
Outage active for {{ '%d:%02d:%02d' | format(hours, minutes, seconds) }}
{% else %}
{{ states.sensor.genmon_outage_status.state }}
{% endif %}
automation:
- alias: 'start power outage'
initial_state: true
trigger:
- platform: state
entity_id: binary_sensor.genmon_power_outage
from: 'off'
to: 'on'
# so ideally wait a little bit for the generator to time out the utility power
# loss, start and operate the automatic transfer switch from utility to generator
# network equipment should be a UPS.. hopefully ISP connectivity is still working
for:
seconds: 45
action:
- service: notify.pushover_louie
data:
message: "Utiliy Power Loss at {{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
title: "HASS"
# maybe also initiate a zwave heal network or something after power is restored to
# from standby generator after a ~45 second interruption? Switch back is pretty
# quick, with only a couple hundred millisecond transfer time in the ATS..
#
- alias: end power outage
initial_state: true
trigger:
- platform: state
entity_id: binary_sensor.genmon_power_outage
from: 'on'
to: 'off'
action:
- service: notify.pushover_louie
data:
message: "Transfer back to utiliy power at {{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
title: "HASS"
############################################################################3
#
# Fun facts (For 22kw generac model)
#
# Per specification, fuel consumption for propane is
# 2.56 gal/hr at 50% load (11KW)
# 3.87 gal/hr at 100% load (22KW)
#
# Per genmon code, the 3 term polynomial to compute this is: [0, 2.74, 1.16]
#
# gal/hr = (T0 * pctLoad^2) + (T1 * pctLoad) + T3
# gal/hr = 2.74 * pctLoad + 1.16
#
# This is a pretty good fit to the spec sheet -
# 50% = 2.53 gal/hr (vs 2.56 spec)
# 100% = 3.90 gal/hr (vs 3.87 spec)
#
# Home assistant could compute a gallons/hour rate in a template sensor if this seems useful.