"TurboTax for Clean Energy Electrification" — a mobile-first web app that photographs home appliances, utility bills, and breaker panels, uses Google Gemini to extract their specs, and produces a phased, geo-targeted clean-energy roadmap with real local incentives.
Built for a hackathon (OneEthos + Gemini API tracks).
- Next.js 16 (App Router, TypeScript, Turbopack)
- Tailwind CSS v4
@google/genai— Gemini 3.5 Flash for multimodal appliance-label parsing@supabase/supabase-js+@supabase/ssr— optional accounts (email/password) and cross-device sync- No account required: state lives client-side in React Context +
localStorageby default; signing in swaps the same state onto a Supabase-hosted Postgres row
npm install
cp .env.example .env.local # then fill in GEMINI_API_KEY below
npm run devOpen http://localhost:3000.
The camera → audit flow calls /api/audit, a server-side Next.js route handler that calls Gemini 3.5 Flash. It needs a real key in .env.local:
GEMINI_API_KEY=your_key_here
Get a key at aistudio.google.com/apikey. Without a key, everything except the camera/audit flow still works (onboarding, dashboard, sorting, Target Bill simulator, and manual appliance entry all run entirely on client-side/mock data).
Signing in is never required — the app works fully on localStorage for guests. Setting up Supabase adds "save your roadmap to an account" on top. Without it configured, the "Sign in" link simply doesn't render.
- Create a project at supabase.com/dashboard. In Project Settings → API, copy the Project URL and anon/public key into
.env.local:NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key - In the SQL Editor, run:
create table if not exists public.user_state ( user_id uuid primary key references auth.users(id) on delete cascade, state jsonb not null default '{}'::jsonb, updated_at timestamptz not null default now() ); alter table public.user_state enable row level security; create policy "user_state_select_own" on public.user_state for select to authenticated using (auth.uid() = user_id); create policy "user_state_insert_own" on public.user_state for insert to authenticated with check (auth.uid() = user_id); create policy "user_state_update_own" on public.user_state for update to authenticated using (auth.uid() = user_id) with check (auth.uid() = user_id); create policy "user_state_delete_own" on public.user_state for delete to authenticated using (auth.uid() = user_id); grant usage on schema public to authenticated; grant select, insert, update, delete on public.user_state to authenticated; create or replace function public.set_user_state_updated_at() returns trigger language plpgsql as $$ begin new.updated_at = now(); return new; end; $$; create trigger user_state_set_updated_at before update on public.user_state for each row execute function public.set_user_state_updated_at();
- Authentication → Providers → Email: turn off "Confirm email" — accounts work immediately after sign-up.
Email/password only for now (kept deliberately simple for judging) — no Google OAuth setup needed.
Guest → account data migration: signing in for the first time seeds your account with whatever's currently in localStorage on that device. On every sign-in after that, the account's cloud data wins over local state.
- Onboarding — zip code + "I already have solar" toggle. Sets the local grid baseline and roadmap focus.
- Dashboard — EcoScore, tiered suggestion cards (Tier 1 quick wins → Tier 3 structural), 4 sort modes, Target Bill simulator, Reject/recalibrate, optional sign-in.
- Camera capture — double-scan (context shot + data-plate shot) → Gemini extraction → confirm → added to roadmap. Falls back to a manual entry form after 3 unreadable scans, or any time via the always-visible manual-entry button.
app/— routes, layout, the/api/audit(Gemini) route handlercomponents/— onboarding, dashboard, camera, auth, and sharedui/primitivescontext/—AppStateContext(reducer + local/remote persistence) andAuthContext(Supabase session)lib/supabase/— the browser Supabase client factory + theisSupabaseConfigured()guardutils/— pure logic: sorting, EcoScore/Target-Bill calculations, incentive matching, local + remote storagedata/— static lookup tables: local incentives, category→tier/price mapping, grid-cleanliness baselines, mock demo datatypes/— shared TypeScript typesproxy.ts— Supabase session-cookie refresh (Next.js 16 renamedmiddleware.ts→proxy.ts)