import { create } from 'zustand'; import { persist } from 'zustand/middleware'; // Increment this when default cameras/config changes to force refresh const CONFIG_VERSION = 9; export interface ThermostatConfig { entityId: string; name: string; location?: string; } export interface LightConfig { entityId: string; name: string; room: string; } export interface LockConfig { entityId: string; name: string; } export interface PersonConfig { entityId: string; name: string; avatarUrl?: string; } export interface CameraConfig { name: string; displayName: string; go2rtcStream: string; frigateCamera?: string; } export interface DashboardConfig { // Selected entities thermostats: ThermostatConfig[]; lights: LightConfig[]; locks: LockConfig[]; alarm: string | null; calendar: string | null; todoList: string | null; people: PersonConfig[]; packageSensor: string | null; // Cameras (manual config for now since they come from go2rtc, not HA) cameras: CameraConfig[]; personDetectionEntities: string[]; // Dashboard settings go2rtcUrl: string; frigateUrl: string; jellyfinUrl: string; jellyfinApiKey: string | null; // Setup completed flag setupCompleted: boolean; // Config version - used to detect when defaults change configVersion: number; } interface SettingsState { config: DashboardConfig; // Actions setThermostats: (thermostats: ThermostatConfig[]) => void; setLights: (lights: LightConfig[]) => void; setLocks: (locks: LockConfig[]) => void; setAlarm: (alarm: string | null) => void; setCalendar: (calendar: string | null) => void; setTodoList: (todoList: string | null) => void; setPeople: (people: PersonConfig[]) => void; setPackageSensor: (sensor: string | null) => void; setCameras: (cameras: CameraConfig[]) => void; setPersonDetectionEntities: (entities: string[]) => void; setGo2rtcUrl: (url: string) => void; setFrigateUrl: (url: string) => void; setJellyfinUrl: (url: string) => void; setJellyfinApiKey: (key: string | null) => void; setSetupCompleted: (completed: boolean) => void; // Bulk update updateConfig: (partial: Partial) => void; resetConfig: () => void; } const defaultConfig: DashboardConfig = { thermostats: [ { entityId: 'climate.kitchen_side', name: 'Kitchen side' }, { entityId: 'climate.master_side', name: 'Master side' }, ], lights: [ { entityId: 'light.back_porch_master', name: 'Back porch master', room: 'Outside' }, { entityId: 'light.master_light', name: 'Bedroom light', room: 'Master' }, { entityId: 'light.chris_lamp', name: 'Chris lamp', room: 'Master' }, { entityId: 'light.front_floods', name: 'Front flood lights', room: 'Outside' }, ], locks: [], alarm: null, calendar: 'calendar.family', todoList: 'todo.shopping_list', people: [], packageSensor: 'binary_sensor.package_detected', cameras: [ // Online cameras { name: 'FPE', displayName: 'Front Porch Entry', go2rtcStream: 'FPE', frigateCamera: 'FPE' }, { name: 'Porch_Downstairs', displayName: 'Porch Downstairs', go2rtcStream: 'Porch_Downstairs', frigateCamera: 'Porch_Downstairs' }, { name: 'Front_Porch', displayName: 'Front Porch', go2rtcStream: 'Front_Porch', frigateCamera: 'Front_Porch' }, { name: 'Driveway_door', displayName: 'Driveway Door', go2rtcStream: 'Driveway_door', frigateCamera: 'Driveway_door' }, { name: 'Street_side', displayName: 'Street Side', go2rtcStream: 'Street_side', frigateCamera: 'Street_side' }, { name: 'Backyard', displayName: 'Backyard', go2rtcStream: 'Backyard', frigateCamera: 'Backyard' }, { name: 'House_side', displayName: 'House Side', go2rtcStream: 'House_side', frigateCamera: 'House_side' }, { name: 'Driveway', displayName: 'Driveway', go2rtcStream: 'Driveway', frigateCamera: 'Driveway' }, { name: 'WyzePanV3', displayName: 'Wyze Pan V3', go2rtcStream: 'WyzePanV3', frigateCamera: 'WyzePanV3' }, // Thingino cameras { name: 'BackDoor', displayName: 'Back Door', go2rtcStream: 'BackDoor', frigateCamera: 'BackDoor' }, { name: 'Parlor', displayName: 'Parlor', go2rtcStream: 'Parlor', frigateCamera: 'Parlor' }, { name: 'Livingroom', displayName: 'Living Room', go2rtcStream: 'Livingroom', frigateCamera: 'Livingroom' }, ], personDetectionEntities: [ 'binary_sensor.fpe_person_occupancy', 'binary_sensor.porch_downstairs_person_occupancy', 'binary_sensor.front_porch_person_occupancy', 'binary_sensor.driveway_door_person_occupancy', 'binary_sensor.driveway_person_occupancy', 'binary_sensor.backyard_person_occupancy', 'binary_sensor.street_side_person_occupancy', 'binary_sensor.house_side_person_occupancy', ], go2rtcUrl: 'http://192.168.1.241:1985', frigateUrl: 'http://192.168.1.241:5000', jellyfinUrl: 'http://192.168.1.49:8096', jellyfinApiKey: null, setupCompleted: false, configVersion: CONFIG_VERSION, }; export const useSettingsStore = create()( persist( (set) => ({ config: { ...defaultConfig }, setThermostats: (thermostats) => set((state) => ({ config: { ...state.config, thermostats } })), setLights: (lights) => set((state) => ({ config: { ...state.config, lights } })), setLocks: (locks) => set((state) => ({ config: { ...state.config, locks } })), setAlarm: (alarm) => set((state) => ({ config: { ...state.config, alarm } })), setCalendar: (calendar) => set((state) => ({ config: { ...state.config, calendar } })), setTodoList: (todoList) => set((state) => ({ config: { ...state.config, todoList } })), setPeople: (people) => set((state) => ({ config: { ...state.config, people } })), setPackageSensor: (sensor) => set((state) => ({ config: { ...state.config, packageSensor: sensor } })), setCameras: (cameras) => set((state) => ({ config: { ...state.config, cameras } })), setPersonDetectionEntities: (entities) => set((state) => ({ config: { ...state.config, personDetectionEntities: entities } })), setGo2rtcUrl: (url) => set((state) => ({ config: { ...state.config, go2rtcUrl: url } })), setFrigateUrl: (url) => set((state) => ({ config: { ...state.config, frigateUrl: url } })), setJellyfinUrl: (url) => set((state) => ({ config: { ...state.config, jellyfinUrl: url } })), setJellyfinApiKey: (key) => set((state) => ({ config: { ...state.config, jellyfinApiKey: key } })), setSetupCompleted: (completed) => set((state) => ({ config: { ...state.config, setupCompleted: completed } })), updateConfig: (partial) => set((state) => ({ config: { ...state.config, ...partial } })), resetConfig: () => set({ config: defaultConfig }), }), { name: 'dashboard-settings', version: CONFIG_VERSION, migrate: (persistedState: unknown, _version: number) => { const state = persistedState as { config: DashboardConfig } | undefined; // If no persisted state, use defaults if (!state?.config) { return { config: { ...defaultConfig } }; } // Merge: start with defaults, overlay user config, then force-update certain fields const userConfig = state.config; return { config: { ...defaultConfig, // Preserve user-configured entities if they exist thermostats: userConfig.thermostats?.length > 0 ? userConfig.thermostats : defaultConfig.thermostats, lights: userConfig.lights?.length > 0 ? userConfig.lights : defaultConfig.lights, locks: userConfig.locks?.length > 0 ? userConfig.locks : defaultConfig.locks, people: userConfig.people?.length > 0 ? userConfig.people : defaultConfig.people, alarm: userConfig.alarm ?? defaultConfig.alarm, calendar: userConfig.calendar ?? defaultConfig.calendar, todoList: userConfig.todoList ?? defaultConfig.todoList, packageSensor: userConfig.packageSensor ?? defaultConfig.packageSensor, go2rtcUrl: userConfig.go2rtcUrl || defaultConfig.go2rtcUrl, frigateUrl: userConfig.frigateUrl || defaultConfig.frigateUrl, jellyfinUrl: userConfig.jellyfinUrl || defaultConfig.jellyfinUrl, jellyfinApiKey: userConfig.jellyfinApiKey ?? defaultConfig.jellyfinApiKey, // Always use latest camera defaults (user can't edit these in UI anyway) cameras: defaultConfig.cameras, personDetectionEntities: userConfig.personDetectionEntities?.length > 0 ? userConfig.personDetectionEntities : defaultConfig.personDetectionEntities, setupCompleted: userConfig.setupCompleted ?? false, configVersion: CONFIG_VERSION, }, }; }, } ) ); // Selector hooks export const useConfig = () => useSettingsStore((state) => state.config); export const useThermostats = () => useSettingsStore((state) => state.config.thermostats); export const useLights = () => useSettingsStore((state) => state.config.lights); export const useLocks = () => useSettingsStore((state) => state.config.locks); export const useAlarmEntity = () => useSettingsStore((state) => state.config.alarm); export const useCalendarEntity = () => useSettingsStore((state) => state.config.calendar); export const useTodoEntity = () => useSettingsStore((state) => state.config.todoList); export const usePeople = () => useSettingsStore((state) => state.config.people); export const useCameras = () => useSettingsStore((state) => state.config.cameras); // Helper to get lights grouped by room export const useLightsByRoom = () => { const lights = useLights(); return lights.reduce((acc, light) => { if (!acc[light.room]) { acc[light.room] = []; } acc[light.room].push(light); return acc; }, {} as Record); };