Monitor your Generac generator with Home Assistant

You might read further along in the thread; you shouldn’t have a rest sensor showing up; it’s all MQTT these days. For reference, here’s what my configuration looks like these days.

generator.yaml
################################################################
## Package / Generator
################################################################
#
#  Version 0.2
#
#  (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.

#  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.

#  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.

#  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]>
#  10 Dec 2020

################################################
## 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: mqtt
    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 Status

  - platform: mqtt
    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'
    name: Genmon Outage Utility Voltage
    value_template: '{{ value | regex_replace(" V", "") | float | round(0) }}'
    unit_of_measurement: "VAC"

  # status related sensors
  - platform: mqtt
    unique_id: "713eb4b9-feec-4425-8c83-84a9f486aae7"
    expire_after: 300   # genmon MQTT flushes out MQTT updated every 60 seconds
    name: Genmon Generator Battery Raw
    state_topic: '19916/generator/Status/Engine/Battery Voltage'
    unit_of_measurement: "V"
    value_template: '{{ value | regex_replace(" V", "") | float | round(1) }}'

  - platform: filter
    name: Genmon Generator Battery
    entity_id: sensor.genmon_generator_battery_raw
    filters:
      - filter: time_throttle
        window_size: "00:15"

  - platform: mqtt
    unique_id: "f3a8aedd-206b-4e5f-baa9-677496ee8d3c"
    expire_after: 300   # genmon MQTT flushes out MQTT updated every 60 seconds
    name:  Genmon Generator RPM
    unit_of_measurement: "RPM"
    state_topic: '19916/generator/Status/Engine/RPM'
    value_template: '{{ value | int }}'

  - platform: mqtt
    unique_id: "cbdc9c28-5748-4aef-91bc-5b5ec95ed545"
    expire_after: 300   # genmon MQTT flushes out MQTT updated every 60 seconds
    name: Genmon Generator Frequency
    unit_of_measurement: "HZ"
    state_topic: '19916/generator/Status/Engine/Frequency'
    value_template: '{{ value | regex_replace(" Hz","") | float }}'

  - platform: mqtt
    unique_id: "f5f462db-e16d-4596-9bd3-232b70fa5038"
    expire_after: 300   # genmon MQTT flushes out MQTT updated every 60 seconds
    name: Genmon Generator Output Voltage
    unit_of_measurement: "VAC"
    state_topic: '19916/generator/Status/Engine/Output Voltage'
    value_template: '{{ value | regex_replace(" V","") | float | round(1) }}'

  # current measrement is garbage on 22KW product, apparently
  - platform: mqtt
    unique_id: "10e1def6-35fd-4922-932a-90f25abb7f29"
    expire_after: 300   # genmon MQTT flushes out MQTT updated every 60 seconds
    name: Genmon Generator Output Current
    unit_of_measurement: "A"
    state_topic: '19916/generator/Status/Engine/Output Current'
    value_template: '{{ value | regex_replace(" A","") | float | round(1) }}'
        
  - platform: mqtt
    unique_id: "914a4e97-54a0-41e7-bbeb-a256134e5e35"
    expire_after: 300   # genmon MQTT flushes out MQTT updated every 60 seconds
    name: Genmon Generator Output Power
    unit_of_measurement: "W"
    state_topic: '19916/generator/Status/Engine/Output Power (Single Phase)'
    # this is normalized to watts from kilowatts
    value_template: '{{ value | regex_replace(" kW","") | float * 1000 | round(0) }}'

  - platform: mqtt
    unique_id: "8cb64471-9123-48f6-b9be-15c01595981e"
    expire_after: 300   # genmon MQTT flushes out MQTT updated every 60 seconds
    name: Genmon Generator Last Alarm Log
    state_topic: '19916/generator/Status/Last Log Entries/Logs/Alarm Log'
        
  - platform: mqtt
    unique_id: "bce3182a-a525-47dc-937c-8be4e5afc619"
    expire_after: 300   # genmon MQTT flushes out MQTT updated every 60 seconds
    name: Genmon Generator Last Service Log
    state_topic: '19916/generator/Status/Last Log Entries/Logs/Service Log'

  - platform: mqtt
    unique_id: "bf48e972-0b96-489a-9eee-4f2030017c3f"
    expire_after: 300   # genmon MQTT flushes out MQTT updated every 60 seconds
    name: Genmon Generator Last Action
    state_topic: '19916/generator/Status/Last Log Entries/Logs/Start Stop Log'
        
    # maint related sensors
  - platform: mqtt
    unique_id: "bf7cbe92-2b1e-48ca-addd-df963697626c"
    expire_after: 300   # genmon MQTT flushes out MQTT updated every 60 seconds
    name: Genmon Generator Capacity
    state_topic: '19916/generator/Maintenance/Rated kW'
    # normalize to watts
    value_template: '{{ value | float * 1000 | round(1) }}'
    unit_of_measurement: 'W'

  - platform: mqtt
    unique_id: "825657b6-c67a-427a-bee9-7b6ea2182628"
    expire_after: 300   # genmon MQTT flushes out MQTT updated every 60 seconds
    name: Genmon Generator Exercise Time
    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"

  # genmon monitor related sensors
  - platform: mqtt
    unique_id: "792c8096-84a3-4870-ac23-8cc5dcc8f8f0"
    expire_after: 300   # genmon MQTT flushes out MQTT updated every 60 seconds
    name: Genmon Mon CPU Temperature Raw
    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) }}'

  - platform: filter
    name: Genmon Mon CPU Temperature
    entity_id: sensor.genmon_mon_cpu_temperature_raw
    filters:
      - filter: time_throttle
        window_size: "00:10"
    
  - platform: mqtt
    unique_id: "7c398cb0-43bf-469b-b49f-ae8b526cac32"
    expire_after: 300   # genmon MQTT flushes out MQTT updated every 60 seconds
    name: Genmon Mon Uptime
    # maybe parse this out into minutes/hours?
    state_topic: '19916/generator/Monitor/Platform Stats/System Uptime'

  - platform: mqtt
    unique_id: "60b44a17-2535-47da-af10-7f6b45d35d3a"
    expire_after: 300   # genmon MQTT flushes out MQTT updated every 60 seconds
    name: Genmon Mon Load
    unit_of_measurement: '%'
    state_topic: '19916/generator/Monitor/Platform Stats/CPU Utilization'
    value_template: '{{ value | regex_replace(" *%","") | float | round(1) }}'

  # note that these following sensors 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.

  - platform: mqtt
    unique_id: "882cb971-bf3e-4281-a158-bf0d0e3eb4ea"
    expire_after: 600
    name: Genmon 1W Controller Box
    unit_of_measurement: °F
    state_topic: '19916/generator/Status/External Temperature Sensors/28-00000bc56fa5'
    value_template: '{{ value | regex_replace(" *F", "") | float | round(1) }}'

  - platform: mqtt
    unique_id: "1f54deef-b168-431a-aa9d-2af8e1fbf2c7"
    expire_after: 600
    name: Genmon 1W Outside Air
    unit_of_measurement: °F
    state_topic: '19916/generator/Status/External Temperature Sensors/28-020a91770670'
    value_template: '{{ value | regex_replace(" *F", "") | float | round(1) }}'

  - platform: mqtt
    unique_id: "8ca45d5e-91fb-4826-bc75-ea1b3d8046c4"
    expire_after: 600
    name: Genmon TC Generator Body
    unit_of_measurement: °F
    state_topic: '19916/generator/Status/External Temperature Sensors/3b-6d800cc77f01'
    value_template: '{{ value | regex_replace(" *F", "") | float | round(1) }}'

  - platform: mqtt
    unique_id: "545c8624-4428-4791-ba95-df671b74aafe"
    expire_after: 600
    name: Genmon TC Oil Cooler
    unit_of_measurement: °F
    state_topic: '19916/generator/Status/External Temperature Sensors/3b-4c880b6ac367'
    value_template: '{{ value | regex_replace(" *F", "") | float | round(1) }}'

  - platform: mqtt
    unique_id: "d6f20e32-04bf-4155-842f-d68346e3a68b"
    expire_after: 600
    name: Genmon TC Engine Body
    unit_of_measurement: °F
    state_topic: '19916/generator/Status/External Temperature Sensors/3b-4c880b6ac389'
    value_template: '{{ value | regex_replace(" *F", "") | float | round(1) }}'



  - platform: template
    sensors:
      generator_outage_summary:
        # entity_id:
        #   - sensor.genmon_outage_status
        unique_id: "11be76cc-02bc-4868-b033-5efc60a289c5"
        value_template: >-
          {% 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 %}
#
#  probably the most useful sensor - are we in a power outage condition or not?
#
binary_sensor:
  - platform: mqtt
    unique_id: "468414df-32b1-4127-8636-4202a11b880c"
    name: Genmon Power Outage
    state_topic: '19916/generator/Outage/System In Outage'
    payload_on: 'Yes'
    payload_off: 'No'

group:
  generator_stats:
    name: generator
    entities:
      - binary_sensor.genmon_power_outage
      - sensor.genmon_outage_status
      - sensor.genmon_outage_utility_voltage
      - sensor.genmon_generator_battery
      - sensor.genmon_generator_rpm
      - sensor.genmon_generator_frequency
      - sensor.genmon_generator_output_voltage
      - sensor.genmon_generator_output_current
      - sensor.genmon_generator_output_power
      - sensor.genmon_generator_last_alarm_log
      - sensor.genmon_generator_last_service_log
      - sensor.genmon_generator_last_action
      - sensor.genmon_generator_capacity
      - sensor.genmon_generator_exercise_time
      - sensor.genmon_mon_cpu_temperature
      - sensor.genmon_mon_uptime
      - sensor.genmon_mon_load
      - sensor.power_volts
      - sensor.power_total

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.
#

Note that the MQTT topic prefix I’m using is not default. I also added in some filter sensors to smooth out frequently changing sensor values (like starter battery voltage) that limits the update rate. On my recorder configuration, I don’t store entity_ids with a _raw suffix to the local Home Assistant database; that’s the point of what’s going on here…

Hope this helps.