-- Postgres database for lamp-control system CREATE TABLE users ( id SERIAL PRIMARY KEY, first_name VARCHAR(50), last_name VARCHAR(50), role SMALLINT NOT NULL DEFAULT 0 CHECK (role BETWEEN 0 AND 3), -- 0=viewer, 1=user, 2=operator, 3=administrator created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, last_login TIMESTAMPTZ, banned_at TIMESTAMPTZ, deleted_at TIMESTAMPTZ ); CREATE TABLE user_sessions ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(id), session_token VARCHAR(255) UNIQUE NOT NULL, expires_at TIMESTAMPTZ NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE zones ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted_at TIMESTAMPTZ ); CREATE TABLE lamp_profiles ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, voltage INTEGER NOT NULL, -- mV (millivolts) current INTEGER NOT NULL, -- mA (milliamps) runtime INTEGER NOT NULL, -- minutes created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted_at TIMESTAMPTZ ); CREATE TABLE sensors ( id SERIAL PRIMARY KEY, device_id VARCHAR(36) UNIQUE, gb_id VARCHAR(36) UNIQUE, latitude DECIMAL(9,6), longitude DECIMAL(9,6), zone_id INT REFERENCES zones(id), created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted_at TIMESTAMPTZ ); CREATE TABLE lamps_relay_boxes ( id SERIAL PRIMARY KEY, device_id VARCHAR(36) UNIQUE, -- uuid gb_id VARCHAR(36) UNIQUE, -- uuid identification_number VARCHAR(50) UNIQUE, -- TODO: calculate status instead of storing it? status SMALLINT NOT NULL DEFAULT 0 CHECK (status BETWEEN 0 AND 2), -- 0=off, 1=on, 2=error latitude DECIMAL(9,6), longitude DECIMAL(9,6), lamp_post_number VARCHAR(50) NOT NULL, running_minutes INTEGER NOT NULL DEFAULT 0, zone_id INT REFERENCES zones(id), profile_id INT REFERENCES lamp_profiles(id), created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted_at TIMESTAMPTZ ); CREATE INDEX idx_lamp_relay_boxes_zone_id ON lamp_relay_boxes(zone_id); -- TODO: maybe calculate running minutes based on metrics instead of status changes? -- Function to calculate running minutes -- Modified function name and logic CREATE OR REPLACE FUNCTION update_lamp_relay_box_running_minutes() RETURNS TRIGGER AS $$ BEGIN IF NEW.status = 1 AND OLD.status = 0 THEN UPDATE lamp_relay_boxes SET running_minutes = running_minutes + EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - OLD.updated_at)) / 60 WHERE id = NEW.id; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; -- Trigger to update running minutes when lamp_relay_box status changes -- Modified trigger name CREATE TRIGGER trg_update_lamp_relay_box_running_minutes AFTER UPDATE OF status ON lamp_relay_boxes FOR EACH ROW EXECUTE FUNCTION trg_update_lamp_relay_box_running_minutes(); CREATE TABLE lamp_relay_box_logs ( id SERIAL PRIMARY KEY, lamp_relay_box_id INT NOT NULL REFERENCES lamp_relay_boxes(id), status SMALLINT NOT NULL, -- 0=off, 1=on, 2=error event_type SMALLINT NOT NULL, -- 0=manual, 1=scheduled, 2=error, 3=gb created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_lamp_relay_box_logs_lamp_relay_box_id_created_at ON lamp_relay_box_logs(lamp_relay_box_id, created_at); CREATE TABLE metrics ( id SERIAL PRIMARY KEY, lamp_relay_box_id INTEGER NOT NULL REFERENCES lamp_relay_boxes(id), metric SMALLINT NOT NULL CHECK (metric BETWEEN 0 AND 6), -- 0=current, 1=voltage, 2=power, 3=energy, 4=lux, 5=brightness, 6=motions value INTEGER NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_metrics_lamp_relay_box_id_metric_created_at ON metrics(lamp_relay_box_id, metric, created_at); -- Create a view for real-time lamp_relay_box status CREATE VIEW real_time_lamp_relay_box_status AS SELECT l.id, l.identification_number, l.status, -- TODO: calculate status instead of storing it? l.latitude, l.longitude, lux.value AS lux, motions.value AS motions, power.value AS power, current.value AS current, voltage.value AS voltage, energy.value AS energy, brightness.value AS brightness, GREATEST(lux.created_at, motions.created_at, power.created_at, current.created_at, voltage.created_at, energy.created_at, brightness.created_at) AS last_updated FROM lamp_relay_boxes l LEFT JOIN LATERAL ( SELECT value, created_at FROM metrics WHERE lamp_relay_box_id = l.id AND metric = 0 -- current ORDER BY created_at DESC LIMIT 1 ) current ON true LEFT JOIN LATERAL ( SELECT value, created_at FROM metrics WHERE lamp_relay_box_id = l.id AND metric = 1 -- voltage ORDER BY created_at DESC LIMIT 1 ) voltage ON true LEFT JOIN LATERAL ( SELECT value, created_at FROM metrics WHERE lamp_relay_box_id = l.id AND metric = 2 -- power ORDER BY created_at DESC LIMIT 1 ) power ON true LEFT JOIN LATERAL ( SELECT value, created_at FROM metrics WHERE lamp_relay_box_id = l.id AND metric = 3 -- energy ORDER BY created_at DESC LIMIT 1 ) energy ON true LEFT JOIN LATERAL ( SELECT value, created_at FROM metrics WHERE lamp_relay_box_id = l.id AND metric = 4 -- lux ORDER BY created_at DESC LIMIT 1 ) lux ON true LEFT JOIN LATERAL ( SELECT value, created_at FROM metrics WHERE lamp_relay_box_id = l.id AND metric = 5 -- brightness ORDER BY created_at DESC LIMIT 1 ) brightness ON true LEFT JOIN LATERAL ( SELECT value, created_at FROM metrics WHERE lamp_relay_box_id = l.id AND metric = 6 -- motions ORDER BY created_at DESC LIMIT 1 ) motions ON true; -- Function to update lamp_relay_box status based on metrics CREATE OR REPLACE FUNCTION update_lamp_relay_box_status() RETURNS TRIGGER AS $$ BEGIN UPDATE lamp_relay_boxes SET status = CASE WHEN NEW.power_watts > 0 THEN 1 ELSE 0 END, updated_at = NEW.created_at WHERE id = NEW.lamp_relay_box_id; RETURN NEW; END; $$ LANGUAGE plpgsql; -- Trigger to update lamp_relay_box status when new metrics is inserted CREATE TRIGGER trigger_update_lamp_relay_box_status AFTER INSERT ON metrics FOR EACH ROW EXECUTE FUNCTION update_lamp_relay_box_status(); CREATE TABLE schedules ( id SERIAL PRIMARY KEY, type SMALLINT NOT NULL, -- 0=manual, 1=scheduled start_time TIME NOT NULL, end_time TIME NOT NULL, status SMALLINT NOT NULL, -- 0=on, 1=off days_of_week bit(7) NOT NULL, -- Bitmask for days of the week created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted_at TIMESTAMPTZ ); CREATE TABLE zone_schedules ( id SERIAL PRIMARY KEY, zone_id INT NOT NULL REFERENCES zones(id), schedule_id INTEGER REFERENCES schedules(id), created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted_at TIMESTAMPTZ ); CREATE INDEX idx_zone_schedules_zone_id ON zone_schedules(zone_id); CREATE TABLE manual_overrides ( id SERIAL PRIMARY KEY, zone_id INT NOT NULL REFERENCES zones(id), user_id INT NOT NULL REFERENCES users(id), start_time TIMESTAMPTZ NOT NULL, end_time TIMESTAMPTZ NOT NULL, type SMALLINT NOT NULL, -- 0=turn_off, 1=turn_on reason VARCHAR(500), created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted_at TIMESTAMPTZ ); CREATE INDEX idx_manual_overrides_zone_id_start_time ON manual_overrides(zone_id, start_time); CREATE TABLE alerts ( id SERIAL PRIMARY KEY, lamp_relay_box_id INT NOT NULL REFERENCES lamp_relay_boxes(id), type SMALLINT NOT NULL, description VARCHAR(500), status SMALLINT NOT NULL DEFAULT 0, -- 0=active, 1=acknowledged, 2=resolved acknowledged_by INTEGER REFERENCES users(user_id), acknowledged_at TIMESTAMPTZ, resolved_by INTEGER REFERENCES users(user_id), resolved_at TIMESTAMPTZ, comments TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE maintenance_logs ( id SERIAL PRIMARY KEY, lamp_relay_box_id INTEGER REFERENCES lamp_relay_boxes(lamp_relay_box_id), user_id INTEGER REFERENCES users(user_id), action_type SMALLINT NOT NULL, -- 0=inspection, 1=repair, 2=replacement description TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- Index for querying by lamp_relay_box in chronological order CREATE INDEX idx_maintenance_logs_lamp_relay_box_id_created_at ON maintenance_logs(lamp_relay_box_id, created_at); -- Index for querying by user in chronological order CREATE INDEX idx_maintenance_logs_user_id_created_at ON maintenance_logs(user_id, created_at);