Skip to content

tomhoag/GenStat

Repository files navigation

Kohler GenStat

GenStat App Icon

A complete home generator monitoring system: a Raspberry Pi reads real-time data from a Kohler transfer switch over RS-232 and publishes state changes to Supabase and Homebridge (HomeKit). A SwiftUI iPhone app displays the current status, runtime history, and event log, and dynamically changes its icon to reflect the generator state. The system sends APNs push notifications for outages and critical events, and includes Home Screen and Lock Screen widgets for at-a-glance status.



Why This Exists

Residential standby generators run infrequently — typically a weekly exercise cycle and the occasional power outage. Between those events they sit idle, and most homeowners have no easy way to confirm the system is healthy without physically walking to the generator or the transfer switch panel to check the status LEDs.

This creates several blind spots:

  • Missed exercise cycles — The Kohler RDT transfer switch has a known firmware issue where the weekly exercise schedule is cleared after a transfer event. Without visibility, the schedule may go unset for weeks or months without the homeowner knowing.
  • Silent failures — If the generator fails to start during an outage, the homeowner may not know until they notice the lights are out. There is no built-in notification system.
  • No outage history — The transfer switch has no accessible log. There is no way to know when the last outage occurred, how long it lasted, or how many hours the generator has accumulated.
  • Maintenance timing — Generator manufacturers recommend service intervals based on runtime hours, but tracking those hours manually against a machine that runs for 20 minutes a week is impractical.

GenStat solves this by providing at-a-glance visibility into the operational state of the system. The monitoring service on the Raspberry Pi determines the current state from live voltage readings and publishes every state change to Supabase and Homebridge (HomeKit). The iOS app reads the Supabase database, presents the information in a clear, glanceable format, dynamically changes its app icon to reflect the current generator state, and sends push notifications when the generator enters a critical state or when an outage begins or ends. Home Screen and Lock Screen widgets provide persistent status visibility without opening the app.

The system catches all four meaningful states:

State Meaning
Normal Utility power present, generator idle — everything is fine
Weekly Test Generator running its exercise cycle — both voltages present
Outage Utility power lost, generator supplying the house
Critical Utility power lost AND generator not running — immediate attention required

⚠️ Safety Warning

Caution

The monitoring hardware requires physical access to the interior of an automatic transfer switch enclosure. This is extremely dangerous work that can result in severe injury or death.

An automatic transfer switch contains live mains voltage at all times — including on the utility input terminals — even when the generator is off and the circuit breakers inside the panel are open. The utility feed entering the enclosure from the top cannot be de-energized without disconnecting power at the utility meter. Contact with these terminals will cause severe injury or death.

All electrical work associated with this project must be performed by a licensed electrician. If you are not a licensed electrician, do not open the transfer switch enclosure, do not route cables through it, and do not connect anything to the terminals or circuit boards inside.

The software components of this project — the Python monitoring script, the iOS app, the Homebridge integration, and the Supabase backend — can all be developed and tested independently without touching the electrical hardware.


Repository Structure

GenStat/                              ← repo root
├── README.md                         # This file — project overview
├── Secrets.xcconfig                  # Actual credentials — gitignored, never committed
├── Secrets.xcconfig.template         # Template for new developers — committed
├── GenStat.xcodeproj/
├── GenStat/                          # iOS app source (see GenStat/README.md)
│   ├── Models/
│   ├── Services/
│   ├── Views/
│   └── README.md
├── GenStatWidget/                    # WidgetKit extension — Home Screen and Lock Screen widgets
├── GenStatTests/                     # Swift Testing unit tests
├── monitoring/                       # Raspberry Pi monitoring service (see monitoring/README.md)
│   ├── generator_monitor.py
│   ├── install.sh
│   ├── requirements.txt
│   └── README.md
└── supabase/
    └── schema.sql                    # Database table definitions and RLS policies

Note: All paths shown above reflect the expected repository structure. Verify that actual paths on your Raspberry Pi deployment match before running the monitoring service or install script.


Setup

1. Create your secrets file

Both the iOS app and the Python monitoring script read credentials from a single Secrets.xcconfig file in the project root. This file is gitignored and must be created manually on each machine.

cp Secrets.xcconfig.template Secrets.xcconfig

Edit Secrets.xcconfig and replace the placeholder values with your actual Supabase project URL and publishable API key:

SUPABASE_URL = https://your-project.supabase.co
SUPABASE_KEY = sb_publishable_...

Note: Secrets.xcconfig must never be committed to the repository. It is listed in .gitignore. Each developer and each deployment (including the Raspberry Pi) must have its own copy.

2. Set up Supabase

Create a Supabase project and set up the three tables described in the Database Schema section below. The complete SQL for all tables, indexes, triggers, and RLS policies is provided in supabase/schema.sql — run it in the Supabase SQL editor to set up the entire schema at once.

3. Component-specific setup


Database Schema

All monitoring data is stored in Supabase (hosted PostgreSQL with REST API). Three tables are used:

generator_status (single row, id = 1)

Column Type Description
id int Always 1
updated_at timestamptz Last backend update
current_state text One of: normal, weekly_test, outage, critical, unknown
utility_voltage float Mains voltage
generator_voltage float Generator output voltage
generator_runtime_hours float Lifetime runtime hours (outage only, excludes exercise)
last_exercise_at timestamptz Last completed exercise
last_outage_at timestamptz Most recent outage start
last_outage_duration_seconds int Most recent outage duration
exercise_schedule_check_needed boolean Flag for exercise schedule reminder
last_service_hours float Runtime hours at last service
service_interval_hours float Hours between services (default 200)
service_check_needed boolean Flag for service reminder

generator_events (append-only log)

Column Type Description
id int Auto-incrementing primary key
created_at timestamptz When the event was recorded
previous_state text State before transition
new_state text State after transition
utility_voltage float Voltage at time of event
generator_voltage float Voltage at time of event
duration_seconds int How long the previous state lasted

device_tokens (push notification registration)

Column Type Description
id bigint Auto-generated identity primary key
token text APNs device token (unique)
platform text Device platform, default 'ios'
active boolean Whether the token is currently valid, default true
created_at timestamptz When the token was first registered
updated_at timestamptz Last update (auto-maintained by trigger)

All tables use Row Level Security with policies allowing anonymous read access (for the iOS app and monitoring service) and anon-role write access (for the monitoring service and device token registration). The complete schema including indexes, triggers, and RLS policies is defined in supabase/schema.sql.


HomeKit Integration

In addition to the Supabase backend, the monitoring service integrates with Apple HomeKit via Homebridge, allowing generator status to appear natively in the iOS Home app alongside other smart home devices.

Infrastructure

Homebridge runs elsewhere in the home. The monitoring Pi and the Homebridge Pi communicate over the local network via simple HTTP calls — the monitoring Pi sends webhook requests to Homebridge whenever the generator state changes.

Monitoring Pi (basement)
    ↓ HTTP webhook (local network)
Homebridge Pi
    ↓ HomeKit protocol
iOS Home app

HomeKit Accessories

The homebridge-http-webhooks plugin exposes two occupancy sensors in HomeKit:

Accessory Occupied when Unoccupied when
Generator Active Generator is running (weekly test or outage) Generator is idle
Utility Power Utility grid is present Utility grid is down

From these two binary sensors all four system states can be inferred:

Generator Active Utility Power System State
Off On Normal
On On Weekly Test
On Off Outage
Off Off Critical

Homebridge Configuration

The relevant section of the Homebridge config.json:

{
    "platform": "HttpWebHooks",
    "webhook_port": 51828,
    "cache_directory": "/var/lib/homebridge/.webhook-cache",
    "sensors": [
        {
            "id": "generator_active",
            "name": "Generator Active",
            "type": "occupancy"
        },
        {
            "id": "utility_power",
            "name": "Utility Power",
            "type": "occupancy"
        }
    ]
}

Behavior During Network Outage

If the home network is unavailable (e.g. during a power outage where the network equipment is not on a generator-backed circuit), the HomeKit webhook calls will fail silently — the monitoring service logs the error and continues. When the network comes back up, the next state change will update the HomeKit sensors correctly. Supabase updates and the GenStat app follow the same pattern — they work when the network is available and catch up on the next successful connection.


Next Steps

  • Live Activity — An iOS Live Activity showing current status on the Lock Screen during an active outage or exercise cycle
  • Historical charts — Visualize runtime hours, outage frequency, and voltage trends over time using Swift Charts
  • SMS notifications — Send SMS alerts on state changes via Twilio or similar, as a fallback when push delivery depends on network/internet availability
  • Notification preferences — In-app settings to toggle push and SMS notifications on/off, configure the SMS phone number, and select which state transitions trigger alerts
  • Extended telemetry — Connect to the generator controller's Modbus interface to monitor battery voltage, coolant temperature, oil pressure, and RPM, in addition to the transfer switch data currently collected
  • Multiple generators — Support monitoring more than one generator from a single app instance
  • Localization — Add string catalog entries for all user-facing text

License

Licensed under the MIT License


Built With Claude

This project was developed collaboratively with Claude, Anthropic's AI assistant, over several sessions in early 2026.

The collaboration followed a clear division of roles. The code — the Python monitoring service, the systemd configuration, the CadQuery enclosure script, the Homebridge webhook integration, the Supabase schema and RLS policies, and the GenStat iOS app specification — was written by Claude. Everything that shaped what got built was driven by the homeowner: defining the goals, asking the questions, providing hardware photographs and measurements, running commands on the Pi and reporting back the actual output, making design decisions when there were options, and pushing back when a proposed solution wasn't right.

The project started as a simple troubleshooting session for a Kohler generator that wouldn't start. Diagnosing a 7-year-old battery failure from fault codes led naturally to the question of ongoing visibility — and that question grew into the full monitoring system documented here. At each stage the homeowner decided what mattered, Claude figured out how to build it, and the back-and-forth between those two things is what produced the result.

It's a reasonable example of what human-AI collaboration looks like in practice: the human brings judgment, context, and real-world grounding; the AI brings breadth of knowledge and the ability to write and iterate on code quickly. Neither half works as well without the other.

About

Kohler Residential Generator Monitor

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors