From 53466eed17794533dc95d728c454f6fbe8be7375 Mon Sep 17 00:00:00 2001 From: rainer Date: Thu, 12 Feb 2026 12:21:21 +0100 Subject: [PATCH] init --- README.md | 0 handlers.lua | 205 ++++++++++++++++++ init.lua | 151 +++++++++++++ mod.conf | 6 + nodes.lua | 37 ++++ overrides.lua | 23 ++ panel.lua | 78 +++++++ switch.lua | 70 ++++++ textures/smart_light_button_off.png | Bin 0 -> 251 bytes textures/smart_light_button_on.png | Bin 0 -> 251 bytes textures/smart_light_case.png | Bin 0 -> 230 bytes textures/smart_light_panel_front_normal.png | Bin 0 -> 6604 bytes textures/smart_light_panel_led_alarm.png | Bin 0 -> 181 bytes .../smart_light_panel_led_supervisory.png | Bin 0 -> 181 bytes textures/smart_light_panel_led_trouble.png | Bin 0 -> 181 bytes textures/smart_light_panel_sides.png | Bin 0 -> 143 bytes textures/smart_light_programmer.png | Bin 0 -> 536 bytes tools.lua | 117 ++++++++++ 18 files changed, 687 insertions(+) create mode 100644 README.md create mode 100644 handlers.lua create mode 100644 init.lua create mode 100644 mod.conf create mode 100644 nodes.lua create mode 100644 overrides.lua create mode 100644 panel.lua create mode 100644 switch.lua create mode 100644 textures/smart_light_button_off.png create mode 100644 textures/smart_light_button_on.png create mode 100644 textures/smart_light_case.png create mode 100644 textures/smart_light_panel_front_normal.png create mode 100644 textures/smart_light_panel_led_alarm.png create mode 100644 textures/smart_light_panel_led_supervisory.png create mode 100644 textures/smart_light_panel_led_trouble.png create mode 100644 textures/smart_light_panel_sides.png create mode 100644 textures/smart_light_programmer.png create mode 100644 tools.lua diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/handlers.lua b/handlers.lua new file mode 100644 index 0000000..4b38171 --- /dev/null +++ b/handlers.lua @@ -0,0 +1,205 @@ +-- 5. FORMSPEC HANDLER +minetest.register_on_player_receive_fields(function(player, formname, fields) + local name = player:get_player_name() + + -- --- ADMIN PANEL --- + if formname:sub(1, 18) == "smart_light:admin_" then + local pos_str = formname:sub(19) + local pos = minetest.string_to_pos(pos_str) + local meta = minetest.get_meta(pos) + local area = meta:get_string("area_id") + + if fields.chan_list then + local evt = minetest.explode_textlist_event(fields.chan_list) + if evt.type == "CHG" or evt.type == "DCL" then meta:set_int("selected_chan_idx", evt.index) end + end + + if fields.save_admin and fields.new_area ~= "" then + meta:set_string("area_id", fields.new_area) + smart_light.registry.areas[fields.new_area] = smart_light.registry.areas[fields.new_area] or { channels = {} } + smart_light.save() + minetest.show_formspec(name, formname, smart_light.get_admin_fs(fields.new_area)) + end + + if fields.del_chan then + local idx = meta:get_int("selected_chan_idx") + if idx > 0 then + local channels = {} + if smart_light.registry.areas[area] then + for c_id, _ in pairs(smart_light.registry.areas[area].channels) do table.insert(channels, c_id) end + table.sort(channels) + end + local target = channels[idx] + if target then + local c_data = smart_light.registry.areas[area].channels[target] + -- Lampen Reset + for p_str, _ in pairs(c_data.nodes or {}) do + local l_pos = minetest.string_to_pos(p_str) + local l_node = minetest.get_node(l_pos) + local original = smart_light.shadow_to_original[l_node.name] or l_node.name + minetest.swap_node(l_pos, {name = original, param2 = l_node.param2}) + local l_meta = minetest.get_meta(l_pos) + l_meta:set_string("sl_area", "") + l_meta:set_string("sl_chan", "") + end + -- Schalter Reset + for s_str, _ in pairs(c_data.switches or {}) do + local s_pos = minetest.string_to_pos(s_str) + minetest.swap_node(s_pos, {name = "smart_light:switch"}) + local s_meta = minetest.get_meta(s_pos) + s_meta:set_string("sl_area", "") + s_meta:set_string("sl_chan", "") + s_meta:set_string("infotext", "") + end + smart_light.registry.areas[area].channels[target] = nil + smart_light.save() + meta:set_int("selected_chan_idx", 0) + minetest.show_formspec(name, formname, smart_light.get_admin_fs(area)) + end + end + end + end + + -- --- SCHALTER PROGRAMMIERER (REBOOTED) --- + if formname:sub(1, 20) == "smart_light:prog_sw_" then + local pos_str = formname:sub(21) + local pos = minetest.string_to_pos(pos_str) + if not pos then return end + + if fields.save_switch then + local area, chan = fields.area_sel, fields.chan_sel + if area == "-- Bitte wählen --" or chan == "-- Bitte wählen --" then + minetest.chat_send_player(name, "FEHLER: Bitte Area UND Kanal auswählen!") + return + end + + local meta = minetest.get_meta(pos) + meta:set_string("sl_area", area) + meta:set_string("sl_chan", chan) + meta:set_string("infotext", "Schalter: " .. area .. ":" .. chan) + + if smart_light.registry.areas[area] and smart_light.registry.areas[area].channels[chan] then + smart_light.registry.areas[area].channels[chan].switches = smart_light.registry.areas[area].channels[chan].switches or {} + smart_light.registry.areas[area].channels[chan].switches[pos_str] = true + smart_light.save() + local level = smart_light.registry.areas[area].channels[chan].last_level or "max" + smart_light.update_switch_visuals(area, chan, level) + minetest.chat_send_player(name, "Schalter gespeichert!") + end + return + end + + if fields.area_sel and fields.area_sel ~= "-- Bitte wählen --" then + minetest.get_meta(pos):set_string("sl_area", fields.area_sel) + minetest.registered_tools["smart_light:programmer"].on_place(player:get_wielded_item(), player, {type="node", under=pos}) + end + end + + -- --- LICHT PROGRAMMIERER --- + if formname:sub(1, 17) == "smart_light:prog_" and formname:sub(1, 20) ~= "smart_light:prog_sw_" then + if fields.save then + local pos_str = formname:sub(18) + local pos = minetest.string_to_pos(pos_str) + local area, chan = fields.area_sel, fields.chan_in + if not area or area == "-- Bitte wählen --" or chan == "" then return end + + minetest.get_meta(pos):set_string("sl_area", area) + minetest.get_meta(pos):set_string("sl_chan", chan) + + local item = player:get_wielded_item() + local t_meta = item:get_meta() + t_meta:set_string("last_area", area) + t_meta:set_string("last_channel", chan) + t_meta:set_string("description", "Programmer Area: " .. area .. "\nChannel: " .. chan) + player:set_wielded_item(item) + + smart_light.registry.areas[area] = smart_light.registry.areas[area] or { channels = {} } + smart_light.registry.areas[area].channels[chan] = smart_light.registry.areas[area].channels[chan] or { nodes = {}, last_level = "max", brightness = "max" } + smart_light.registry.areas[area].channels[chan].nodes[pos_str] = true + smart_light.save() + minetest.chat_send_player(name, "Licht registriert!") + end + end + + -- --- HAUPTMENÜ (PANEL) --- + if formname:sub(1, 17) == "smart_light:main_" then + local pos_str = formname:sub(18) + local pos = minetest.string_to_pos(pos_str) + local area = minetest.get_meta(pos):get_string("area_id") + + if fields.all_off then + for c,_ in pairs(smart_light.registry.areas[area].channels) do smart_light.switch_channel(area, c, "off") end + minetest.registered_nodes["smart_light:controller"].on_rightclick(pos, nil, player) + elseif fields.all_on then + for c,_ in pairs(smart_light.registry.areas[area].channels) do smart_light.switch_channel(area, c, "on") end + minetest.registered_nodes["smart_light:controller"].on_rightclick(pos, nil, player) + else + for k, _ in pairs(fields) do + local cid = k:match("^chan_(.+)$") + if cid then + local count = smart_light.get_count(area, cid) + local status = smart_light.get_status_text(area, cid) + local last_lvl = smart_light.registry.areas[area].channels[cid].last_level or "max" + + local subfs = "size[4,5.5]label[0.5,0.2;Channel: " .. cid .. "]" .. + "label[0.5,0.7;Anzahl Leuchten: " .. count .. "]" .. + "label[0.5,1.2;Status: " .. status .. "]" .. + "label[0.5,1.7;Dimmlevel: " .. last_lvl .. "]" .. + "button[0.5,2.5;1.5,0.8;sub_"..cid.."_on;An]" .. + "button[2.2,2.5;1.0,0.8;sub_"..cid.."_up;D+]" .. + "button[0.5,3.5;1.5,0.8;sub_"..cid.."_off;Aus]" .. + "button[2.2,3.5;1.0,0.8;sub_"..cid.."_down;D-]" .. + "button[0.5,4.7;3,0.6;back;Zurück]" + minetest.show_formspec(name, "smart_light:sub_" .. pos_str, subfs) + return + end + end + end + end + + -- --- SUB-MENÜ --- + if formname:sub(1, 16) == "smart_light:sub_" then + local pos_str = formname:sub(17) + local pos = minetest.string_to_pos(pos_str) + local meta = minetest.get_meta(pos) + local area = meta:get_string("area_id") + if area == "" then area = meta:get_string("sl_area") end + + for k, _ in pairs(fields) do + local cid, action = k:match("^sub_(.+)_([%a%d]+)$") + if cid and action then + smart_light.switch_channel(area, cid, action) + local count = smart_light.get_count(area, cid) + local status = smart_light.get_status_text(area, cid) + local last_lvl = smart_light.registry.areas[area].channels[cid].last_level or "max" + + local subfs = "size[4,5.5]label[0.5,0.2;Channel: " .. cid .. "]" .. + "label[0.5,0.7;Anzahl Leuchten: " .. count .. "]" .. + "label[0.5,1.2;Status: " .. status .. "]" .. + "label[0.5,1.7;Dimmlevel: " .. last_lvl .. "]" .. + "button[0.5,2.5;1.5,0.8;sub_"..cid.."_on;An]" .. + "button[2.2,2.5;1.0,0.8;sub_"..cid.."_up;D+]" .. + "button[0.5,3.5;1.5,0.8;sub_"..cid.."_off;Aus]" .. + "button[2.2,3.5;1.0,0.8;sub_"..cid.."_down;D-]" .. + "button[0.5,4.7;3,0.6;back;Zurück]" + minetest.show_formspec(name, formname, subfs) + return + end + end + if fields.back or fields.exit then + if minetest.get_node(pos).name == "smart_light:controller" then + minetest.registered_nodes["smart_light:controller"].on_rightclick(pos, nil, player) + else minetest.close_formspec(name, formname) end + end + end + + -- --- PANEL SETUP --- + if formname:sub(1, 18) == "smart_light:setup_" then + if fields.set_area and fields.area_id ~= "" then + local pos = minetest.string_to_pos(formname:sub(19)) + minetest.get_meta(pos):set_string("area_id", fields.area_id) + smart_light.registry.areas[fields.area_id] = smart_light.registry.areas[fields.area_id] or { channels = {} } + smart_light.save() + end + end +end) diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..0964901 --- /dev/null +++ b/init.lua @@ -0,0 +1,151 @@ +smart_light = {} +local storage = minetest.get_mod_storage() + +-- Datenstruktur laden +local data = storage:get_string("registry") +smart_light.registry = data ~= "" and minetest.deserialize(data) or { areas = {} } + +smart_light.shadow_to_original = {} +smart_light.original_to_shadows = {} + +-- Definition der Stufen für die Dimm-Logik +smart_light.LEVEL_ORDER = {"off", "low", "mid", "high", "max"} + +function smart_light.save() + storage:set_string("registry", minetest.serialize(smart_light.registry)) +end + +-- Hilfsfunktion: Status-Text (AN/AUS) für UI +function smart_light.get_status_text(area, chan) + if not smart_light.registry.areas[area] or not smart_light.registry.areas[area].channels[chan] then + return "AUS" + end + local level = smart_light.registry.areas[area].channels[chan].last_level or "max" + return (level == "off") and "AUS" or "AN" +end + +-- Hilfsfunktion: Zählt Lichter in einem Channel +function smart_light.get_count(area, chan) + if not smart_light.registry.areas[area] or not smart_light.registry.areas[area].channels[chan] then + return 0 + end + local count = 0 + local target = smart_light.registry.areas[area].channels[chan].nodes or {} + for _ in pairs(target) do + count = count + 1 + end + return count +end + +-- Hilfsfunktion: Entfernt ein Licht aus der Registry +function smart_light.unregister_light(pos, area, chan) + local pos_str = minetest.pos_to_string(pos) + if smart_light.registry.areas[area] and smart_light.registry.areas[area].channels[chan] then + if smart_light.registry.areas[area].channels[chan].nodes then + smart_light.registry.areas[area].channels[chan].nodes[pos_str] = nil + if next(smart_light.registry.areas[area].channels[chan].nodes) == nil then + smart_light.registry.areas[area].channels[chan] = nil + end + smart_light.save() + end + end +end + +-- Hilfsfunktion: Schalter-Visuals aktualisieren +function smart_light.update_switch_visuals(area, chan, level) + if not smart_light.registry.areas[area] or not smart_light.registry.areas[area].channels[chan] then return end + local chan_data = smart_light.registry.areas[area].channels[chan] + if not chan_data.switches then return end + + local node_name = (level == "off") and "smart_light:switch" or "smart_light:switch_active" + + for pos_str, _ in pairs(chan_data.switches) do + local pos = minetest.string_to_pos(pos_str) + local node = minetest.get_node(pos) + if node.name:sub(1, 18) == "smart_light:switch" then + minetest.swap_node(pos, {name = node_name, param2 = node.param2}) + end + end +end + +-- Hilfsfunktion: Admin-Formspec +function smart_light.get_admin_fs(area, selected_idx) + local channels = {} + if smart_light.registry.areas[area] and smart_light.registry.areas[area].channels then + for c_id, _ in pairs(smart_light.registry.areas[area].channels) do + table.insert(channels, c_id) + end + table.sort(channels) + end + local list_str = "" + for _, c_id in ipairs(channels) do + list_str = list_str .. c_id .. " (" .. smart_light.get_count(area, c_id) .. ")," + end + return "size[6,7]label[0.5,0.2;Panel Verwaltung ("..area..")]" .. + "field[0.8,1.2;5,1;new_area;Area-ID ändern;"..area.."]" .. + "textlist[0.5,2.5;5,3;chan_list;"..list_str..";"..(selected_idx or "")..";false]" .. + "button[0.5,6;2.5,1;del_chan;Channel löschen]" .. + "button_exit[3.5,6;2,1;save_admin;Speichern]" +end + +-- Hilfsfunktion: Schaltlogik (RECALL FIXED) +function smart_light.switch_channel(area_id, channel_id, action) + local area = smart_light.registry.areas[area_id] + if not area or not area.channels or not area.channels[channel_id] then return end + + local chan_data = area.channels[channel_id] + + -- Korrekte Initialisierung der Datenstruktur + if not chan_data.nodes then + local old_nodes = table.copy(chan_data) + area.channels[channel_id] = { nodes = old_nodes, last_level = "max", brightness = "max" } + chan_data = area.channels[channel_id] + end + + -- Helligkeits-Gedächtnis sicherstellen + if not chan_data.brightness or chan_data.brightness == "off" then + chan_data.brightness = "max" + end + + local target_level + + if action == "off" then + target_level = "off" + -- Wir speichern target_level NICHT in brightness, um das Gedächtnis zu behalten + elseif action == "on" then + target_level = chan_data.brightness + elseif action == "up" or action == "down" then + local cur = chan_data.brightness + local idx = 1 + for i, v in ipairs(smart_light.LEVEL_ORDER) do if v == cur then idx = i end end + if action == "up" then idx = math.min(idx + 1, 5) else idx = math.max(idx - 1, 2) end + target_level = smart_light.LEVEL_ORDER[idx] + chan_data.brightness = target_level -- Neues Gedächtnis speichern + end + + -- Aktuellen Status für das UI speichern + chan_data.last_level = target_level + + -- Physisches Umschalten + for pos_str, _ in pairs(chan_data.nodes) do + local pos = minetest.string_to_pos(pos_str) + local node = minetest.get_node(pos) + local original = smart_light.shadow_to_original[node.name] or node.name + local shadows = smart_light.original_to_shadows[original] + if shadows and shadows[target_level] then + minetest.swap_node(pos, {name = shadows[target_level], param2 = node.param2}) + end + end + + smart_light.update_switch_visuals(area_id, channel_id, target_level) + smart_light.save() +end + +-- Dateien laden +local path = minetest.get_modpath("smart_light") +dofile(path .. "/nodes.lua") +dofile(path .. "/tools.lua") +dofile(path .. "/panel.lua") +dofile(path .. "/handlers.lua") +dofile(path .. "/switch.lua") +dofile(path .. "/overrides.lua") diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..00b2838 --- /dev/null +++ b/mod.conf @@ -0,0 +1,6 @@ +name = smart_light +description = Steuerung und Dimmen von Lichtern via Shadow-Nodes. +depends = default +optional_depends = areas, myschool, morelights_modern, morelights_vintage +author = Rage87 +version = 1005 diff --git a/nodes.lua b/nodes.lua new file mode 100644 index 0000000..c9e4018 --- /dev/null +++ b/nodes.lua @@ -0,0 +1,37 @@ +-- 1. LAMPEN-REGISTRIERUNG +function smart_light.register_lamp_control(node_name) + local def = minetest.registered_nodes[node_name] + if not def then return end + + local levels = { off = 0, low = 6, mid = 8, high = 12, max = 15 } + local shadows = {} + local base_name = node_name:gsub(":", "_") + + local function cleanup_after_dig(pos, oldnode, oldmetadata) + local area = oldmetadata.fields.sl_area + local chan = oldmetadata.fields.sl_chan + if area and chan then + smart_light.unregister_light(pos, area, chan) + end + end + + for suffix, light_val in pairs(levels) do + local shadow_name = ":smart_light:" .. base_name .. "_" .. suffix + local shadow_def = table.copy(def) + shadow_def.light_source = light_val + shadow_def.groups = table.copy(def.groups or {}) + shadow_def.groups.not_in_creative_inventory = 1 + shadow_def.drop = node_name + shadow_def.after_dig_node = cleanup_after_dig + minetest.register_node(shadow_name, shadow_def) + shadows[suffix] = "smart_light:" .. base_name .. "_" .. suffix + smart_light.shadow_to_original["smart_light:" .. base_name .. "_" .. suffix] = node_name + end + + minetest.override_item(node_name, { + after_dig_node = cleanup_after_dig + }) + + shadows["on"] = node_name + smart_light.original_to_shadows[node_name] = shadows +end diff --git a/overrides.lua b/overrides.lua new file mode 100644 index 0000000..cf87fff --- /dev/null +++ b/overrides.lua @@ -0,0 +1,23 @@ +-- Hier können beliebig viele Lampen registriert werden +minetest.register_on_mods_loaded(function() + smart_light.register_lamp_control("myschool:light") + smart_light.register_lamp_control("morelights_modern:walllamp") + smart_light.register_lamp_control("morelights_modern:post_d") + smart_light.register_lamp_control("morelights_modern:post_l") + smart_light.register_lamp_control("morelights_modern:barlight_c") + smart_light.register_lamp_control("morelights_modern:barlight_s") + smart_light.register_lamp_control("morelights_modern:ceilinglight") + smart_light.register_lamp_control("morelights_modern:canlight_d") + smart_light.register_lamp_control("morelights_modern:canlight_l") + smart_light.register_lamp_control("morelights_modern:tablelamp_d") + smart_light.register_lamp_control("morelights_modern:tablelamp_l") + smart_light.register_lamp_control("morelights_modern:pathlight_d") + smart_light.register_lamp_control("morelights_modern:pathlight_l") + smart_light.register_lamp_control("morelights_vintage:lantern_f") + smart_light.register_lamp_control("morelights_vintage:lantern_c") + smart_light.register_lamp_control("morelights_vintage:lantern_w") + smart_light.register_lamp_control("morelights_vintage:hangingbulb") + smart_light.register_lamp_control("morelights_vintage:chandelier") + + -- smart_light.register_lamp_control("andere_mod:lampe") +end) diff --git a/panel.lua b/panel.lua new file mode 100644 index 0000000..3539b33 --- /dev/null +++ b/panel.lua @@ -0,0 +1,78 @@ +-- 4. CONTROLLER (Das Panel) +minetest.register_node("smart_light:controller", { + description = "Smart Light Controller Panel", + tiles = { + "smart_light_panel_sides.png", "smart_light_panel_sides.png", + "smart_light_panel_sides.png", "smart_light_panel_sides.png", + "smart_light_panel_sides.png", "smart_light_panel_front_normal.png" + }, + drawtype = "nodebox", paramtype = "light", paramtype2 = "facedir", + groups = {cracky = 2, oddly_breakable_by_hand = 1}, + node_box = { type = "fixed", fixed = {{-0.5, -0.5, 0.4, 0.5, 0.5, 0.5}} }, + on_rightclick = function(pos, node, clicker) + local meta = minetest.get_meta(pos) + local area = meta:get_string("area_id") + local name = clicker:get_player_name() + + if area == "" then + local fs = "size[5,3]field[0.8,1;4,1;area_id;Panel Area ID (z.B. Schule);]button_exit[1,2;3,1;set_area;Area festlegen]" + minetest.show_formspec(name, "smart_light:setup_" .. minetest.pos_to_string(pos), fs) + return + end + + local fs = "size[11,9]label[0.5,0.2;Zentrale: " .. area:upper() .. "]" .. + "label[6,0.2;Global:]" .. + "button[7,0;1.2,0.6;all_off;OFF]button[8.5,0;1.2,0.6;all_on;ON]" + + local y, x, count, channels = 1.2, 0.5, 0, {} + if smart_light.registry.areas[area] and smart_light.registry.areas[area].channels then + for c_id, _ in pairs(smart_light.registry.areas[area].channels) do table.insert(channels, c_id) end + table.sort(channels) + for _, c_id in ipairs(channels) do + -- Status AN/AUS statt Anzahl + local status = smart_light.get_status_text(area, c_id) + fs = fs .. "button["..x..","..y..";2.5,0.8;chan_"..c_id..";" .. c_id .. " ("..status..")]" + x, count = x + 2.6, count + 1 + if count % 4 == 0 then x, y = 0.5, y + 1 end + end + else + fs = fs .. "label[0.5,2;Keine Lichter für diese Area registriert.]" + end + minetest.show_formspec(name, "smart_light:main_" .. minetest.pos_to_string(pos), fs) + end, + + after_dig_node = function(pos, oldnode, oldmetadata) + local area = oldmetadata.fields.area_id + if area and area ~= "" and smart_light.registry.areas[area] then + if smart_light.registry.areas[area].channels then + for c_id, c_data in pairs(smart_light.registry.areas[area].channels) do + -- 1. Lampen zurücksetzen + local nodes = c_data.nodes or {} + for p_str, _ in pairs(nodes) do + local l_pos = minetest.string_to_pos(p_str) + local l_node = minetest.get_node(l_pos) + if l_node.name ~= "ignore" then + local original = smart_light.shadow_to_original[l_node.name] or l_node.name + minetest.swap_node(l_pos, {name = original, param2 = l_node.param2}) + local l_meta = minetest.get_meta(l_pos) + l_meta:set_string("sl_area", "") + l_meta:set_string("sl_chan", "") + end + end + -- 2. Schalter zurücksetzen + local switches = c_data.switches or {} + for s_str, _ in pairs(switches) do + local s_pos = minetest.string_to_pos(s_str) + minetest.swap_node(s_pos, {name = "smart_light:switch"}) + local s_meta = minetest.get_meta(s_pos) + s_meta:set_string("sl_area", "") + s_meta:set_string("sl_chan", "") + s_meta:set_string("infotext", "") + end + end + end + smart_light.registry.areas[area] = nil + smart_light.save() + end + end, +}) diff --git a/switch.lua b/switch.lua new file mode 100644 index 0000000..e3a79df --- /dev/null +++ b/switch.lua @@ -0,0 +1,70 @@ +local switch_box = { type = "fixed", fixed = { { -6/32, -6/32, 14/32, 6/32, 6/32, 16/32} } } + +minetest.register_node("smart_light:switch", { + description = "Smart Light Switch", + tiles = { + "smart_light_case.png", "smart_light_case.png", + "smart_light_case.png", "smart_light_case.png", + "smart_light_case.png", "smart_light_case.png^smart_light_button_off.png", + }, + drawtype = "nodebox", paramtype = "light", paramtype2 = "facedir", + sunlight_propagates = true, groups = {cracky=2, crumbly=2}, + node_box = switch_box, + + on_punch = function(pos, node, puncher) + if not puncher:get_player_control().sneak then return end + + local meta = minetest.get_meta(pos) + local area, chan = meta:get_string("sl_area"), meta:get_string("sl_chan") + if area ~= "" and chan ~= "" and smart_light.registry.areas[area] and smart_light.registry.areas[area].channels[chan] then + local count = smart_light.get_count(area, chan) + local status = smart_light.get_status_text(area, chan) + local last_lvl = smart_light.registry.areas[area].channels[chan].last_level or "max" + + local subfs = "size[4,5.5]label[0.5,0.2;Channel: " .. chan .. "]" .. + "label[0.5,0.7;Anzahl Leuchten: " .. count .. "]" .. + "label[0.5,1.2;Status: " .. status .. "]" .. + "label[0.5,1.7;Dimmlevel: " .. last_lvl .. "]" .. + "button[0.5,2.5;1.5,0.8;sub_"..chan.."_on;An]" .. + "button[2.2,2.5;1.0,0.8;sub_"..chan.."_up;D+]" .. + "button[0.5,3.5;1.5,0.8;sub_"..chan.."_off;Aus]" .. + "button[2.2,3.5;1.0,0.8;sub_"..chan.."_down;D-]" .. + "button_exit[0.5,4.7;3,0.6;exit;Schließen]" + minetest.show_formspec(puncher:get_player_name(), "smart_light:sub_" .. minetest.pos_to_string(pos), subfs) + end + end, + + on_rightclick = function(pos, node, clicker) + local meta = minetest.get_meta(pos) + local area, chan = meta:get_string("sl_area"), meta:get_string("sl_chan") + if area ~= "" and chan ~= "" and smart_light.registry.areas[area] and smart_light.registry.areas[area].channels[chan] then + local status = smart_light.get_status_text(area, chan) + smart_light.switch_channel(area, chan, (status == "AUS") and "on" or "off") + end + end, + + after_dig_node = function(pos, oldnode, oldmetadata) + local area, chan = oldmetadata.fields.sl_area, oldmetadata.fields.sl_chan + if area and chan and smart_light.registry.areas[area] and smart_light.registry.areas[area].channels[chan] then + if smart_light.registry.areas[area].channels[chan].switches then + smart_light.registry.areas[area].channels[chan].switches[minetest.pos_to_string(pos)] = nil + smart_light.save() + end + end + end, +}) + +minetest.register_node("smart_light:switch_active", { + description = "Smart Light Switch", + tiles = { + "smart_light_case.png", "smart_light_case.png", + "smart_light_case.png", "smart_light_case.png", + "smart_light_case.png", "smart_light_case.png^smart_light_button_on.png", + }, + drawtype = "nodebox", paramtype = "light", paramtype2 = "facedir", + sunlight_propagates = true, groups = {cracky=2, crumbly=2, not_in_creative_inventory=1}, + node_box = switch_box, drop = "smart_light:switch", + on_punch = minetest.registered_nodes["smart_light:switch"].on_punch, + on_rightclick = minetest.registered_nodes["smart_light:switch"].on_rightclick, + after_dig_node = minetest.registered_nodes["smart_light:switch"].after_dig_node, +}) diff --git a/textures/smart_light_button_off.png b/textures/smart_light_button_off.png new file mode 100644 index 0000000000000000000000000000000000000000..dd0d6bad5d7e220a83f1879dc573526e130bb610 GIT binary patch literal 251 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofvPP)Tsw@SkfJR9T^xl z_H+M9WCijSl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP(n1o zC&U#<`(zegym;}+lPCZG|1T*lJjcNB!1|aXP=c`}$S;_|;n|HeAjjL&#W5t}@Y}Q2 zLJbBy%olq$6#rMAd|rjsxmj~gQvCW*O@@ZYvHBb?Oxs+UU*8sH-kQl+TFIy>Ai+Jk oj(ypl2K|Evm>c^U863K#=U!(?ZMtzQ5@;-gr>mdKI;Vst01$^xI{*Lx literal 0 HcmV?d00001 diff --git a/textures/smart_light_button_on.png b/textures/smart_light_button_on.png new file mode 100644 index 0000000000000000000000000000000000000000..f6d02338972ea8976db8115d35be999f28b6165f GIT binary patch literal 251 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofvPP)Tsw@SkfJR9T^xl z_H+M9WCijSl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP(n1o zC&U#<`(zegym;}+lPCZG|1T*lH2N9ZG;6aIP=c`}$S;_|;n|HeAjjL&#W5t}@Y}Q2 zLJbBy%olq$6#rMAd|rjsxmj~gQvCW*O@@ZYvHBb?Oxs+UU*8sH-kQl+TFIy>Ai+Jk oj(ypl2K|Evm>c^U863K#=U!(?ZMtzQ5@;-gr>mdKI;Vst09Gna)Bpeg literal 0 HcmV?d00001 diff --git a/textures/smart_light_case.png b/textures/smart_light_case.png new file mode 100644 index 0000000000000000000000000000000000000000..35c71254b1c3de2db0e691ab4080223ea913c08f GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvVv45dj$D1FjT2AFf_C%^dwctg88b36GL|e^GI{dkdGqF7zI@r5Y3nPXGFwj<#}JFt$q9-K5^TLp zVQUf$v>qfRr?V>wJUg(efp_8b-U&=)Yq)NB1{m0^Ik2vQS>jYhbJYg*8IJS5GW3ZF Vne6ixTnjXa!PC{xWt~$(69BGcM}hzV literal 0 HcmV?d00001 diff --git a/textures/smart_light_panel_front_normal.png b/textures/smart_light_panel_front_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..a6c244d80dde3f422cd0040dd7ef373a682a8cde GIT binary patch literal 6604 zcmV;-88haIP)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBV0N=ZaPRCwBjo6oOp$#vI1YgN_W=iJ*S>UQEEaf?WPZY%}KCT;Ar z7#O4ki2*VU5b6v)vD@a~aik=Q5N3i}?m@&PzBf8kwPHnOpFl)um&k5RF!?t1Py%wAjISo zH3MsiD!FA;6aZ0qG#P+W3Nc3Zt@;G8Ie8HQkvV8H18wBIV`hj5X8x=Y113PTKIw>x zAbl~%B8d0`?fc$8CzO^6A+Xo}4m000RZ6Yg-Q7`2^#uqquB6v z(VFjaF10V15n|wUI{CtBBuJ!`LhJ9nZkzwEHG(Q{zx_654Yj^9ATxwSq))8P|A!C| z5u!#!gwhITjg*p464?7oYOPrFMbr>cRa$GTX`SyFq1();C1a)1sxOYJB4!xS%s8LU zm>I?DoRF##RA{wPioZYS%(5)h);uy0=;OaTpL@g@wHB6T@dfo=kA$FX+cwV-s=m7T zs*4C9RI9$?68iU`WDoiC-5q8glGb;-Ro|u78jZ@n@6^(B3LpZ@vii@h_M@jDLaUXa z%AR+YWntAc#}i}h$BFrTfZQV)AT4Q*{c^d0L9G?l;6G0hl^%|zR?K`?LJTmHnff{N zKmN-h=zJl-dOA^S<$Aqh2BHvCB&8lvc#aYA$;F=VT5FV2sr~q9kFF2`wZFfN=-PK` zYp8gVo=I$MRL=?9enEOPT&@@PefQ{FS5P>eP5{*AM@>g!iXkB4=d9!c;*l{bf{65} z01=Y2lgyFuM1+`@quf~~hC~R#b3wD2(ML`QArK^>5@^+TZb{3m zQeGWmKL(0~z-9N`b6QTcR=I2!k0c2^efq$;pMP2-1W%5!mwb&e&I~zf=@^Iv!GIK# zXWg9Vaqw(hQ4yLotbc!GcU7g-;ya8?Trtk;^?F61*Y~5Ol~O=K)px#>LaVhu+ZVsq zQC@v#*JbtJ>(Lp4&SP^rt!&q;zt?Pzg`VrwCtg|CwXd+vkZh$VmTmK$(J(`PF9qpg z-zr$&t?|1d1kR_kFI+8z(5pj#@%{Zh)+f-c&8Qer)XX@YPG|^(6#ILnXYV$TW(Z;C ziBYLasYu5ut$CDcT!_&@l^+L>yy#gQT5TYL06ikPzrW|<;bDf{sN-G^74Y2*K`~Qa zfBp5Dkp8Bx|JToBjB}C3ghoW&PO#R>CqDU!37VE=A?KYv??-hKqxDjpOCdkJ!?Jp{ zQ4#h%b2_bP>!7FH02VXD)HCV2uGCsP%3>z1hG94k5S4i>#@Fq?mkx3U41V{5%yQc6 z`S`k>{P_O>Woe?%%C_%>5WVg@C?x2ySSmsYi4-GN?MUFQ60I6d8j5h;ws{mnQ(CDM zEyN{J_Ut82#8FzGjDwy!7ePe$;upV2N{LbnCJ@DsPD+WK^UMRPN^RA11D+5C=OrRX zwNO=Pey0EQU;pV?VnmLr@bu|BoX&TSNQ1I03)kzF*c11J=qdE~R|Fxp+DJ=!R99PT z>?KdKV{~#+jcS!8Mz;R>eb1cFr$@UPLg3$h_AAr?=d^Nt*pTQ&S4C;o=oJlX@bZ8D z5+*{qIvew@)|U1>eTni(Z$_MC|+^$f1GTG5;EAIAqXWD(SiTsLA;Dvc%}2qr?RMpfZM z*beUew;H>9n%Mg>BpXsCqd7jJ;;BMjb2E=EzhvQA#G> z-JQ8!Hv~ZxY9|=Almo&xPmrh6iBdcoN-1cFv<_0XZNEL(SXR=H?vYEPK6~YvZ~huj z|G`f=|CbMN`Mn>q{>R_s;h+4FAE&ptKmR^o`ob5`C3>j0!g*QQ_l#H}q!SH8df2qq z&3T8|%6dLLlbsp|LFfjqCN=6Nr-YPFfZx z7^OI54S|vm6t^r(ukmFf!CXMJp{Cecsn0%8pS|P!#&g*FGy2Yn`jJd{dQUz-MM3}z z{NY#r@NeAxSHAE?*3;dL(6$>PDY-Z`7x9Tc!V9GmQ=Anhr37TG1xl{2J9rs$Ivk=7 za6ryG>w03}Hfo2YBZJ3Ym&c077-t4|lx3J2*XRF=SD9bE{VPlw&B`1@wT4=Aw0L&{Gjh&T45{0WF0m)o z%;|KRK+de8>S!~z+6YOB5@)8Sqd%LO>fi;Vw5;n&?UOdM842U>H3aI<&*(2!N-XFe zdHRoj%=(?@oSpsMpL2fmIdp+0jYuGz?)b`AzVe>k|MI{3@=SIE8w|x~C|C^D3eWIi zO*D+_b|r*(9D#m3Mv09YJegr#Ea+up?6P$T=}=QTJgxIM41QmFmK}*`+Z22D9rXQA zK>}2e4Et~Y6`?k)!S>{TV>`4G2&<4==F4CH@|;+04uLVa?uDhJ$=izKV#k#f;+!B| zfl4u=ORplWLbDmI(v_vCs#rC`k|xNQq@z!6h$ic@5Z4ve3|JQ+PO}n>XiyDU$Y9Pc zk-{P;s#FhaS!nDoeFaFX_ztP7KY#p3e{`JvFTFx7jWr$e*EoJ51eSFH#9{8hC^6D* z>3v6#xD4R)?4&9Jm_VtSk{ipKsI>vsDXKi?=(wVi(!%K!J1(#9L3jW1KfQvgVCE5& zVwkE;jFD0^DIRrw3|drq<&{@v$g3!lM*im!(yW~%i9)S|qzQYEl5sRImy2rx%YqDQ zFG{VAIH01LE3>s0(z48Q=ppC}P;vpBFqKk>DwJl#cVYHF`IA3>B$7@sv=8c*4~%HL zgyLaqX7Jv7`+s7$hA83Mo;^~D9iykoqjPT5Qpo$(dAtwsxo4l_zx~&5y(cGp z>cvmZ9saG~`rCZ)Lm#|}iX+U&`+o30f57w4e}q~q-~ayi`R=z~=P$nV-D5EXLR2*P z=BdyX>e{Q6A5-i3%Y~GNRhz0-vr6P9z4*gjO}$(rXV(!im&}{Z1v{Ni{_o)Gwre1u zrQ9SSC#9sAIGC9*~KPGDPC=4Z9r5hc_k|ImT1Fe_5+k`b(-^jvaa-LAi{LIgO{1aYy;f1N@ ziU_~^>EC^Hmdy+q(Q|gVIzrV=4V?r3>_7Z0pZUyZoK5!MZ(TiJ*A*29O%u{b`|2Us4D6HLD)W~_g_;YfJPO}i(_yTID=O=wRb7^2F+TkKM|kbE*JcKP;e{9Y zzz07#fBrwd{T(bQ|KZR791-EUzx6(T_OqXL*}Ny?sJ!vU8+`oZALm=&{_ni?=J)vP zU;J_=YVBok-}gs(YN$`GS*OY;LJTaYg<9JjlL2@4_sEMczGw(j@Ej8#B*%@C2xgSL z(M+ZfFhYK)HsAc_Hy`1F+d+*|HR}Fv{Kl{I`s=Ur(T_gq{QS@y`M-dkcbCQ&UwrY# z&rlw&SI(z(nkH-4|3+dT01R{BXFZxD(1lP#LzuFab=c_uDEEg&bf_vJ(CVS#jnBM$ zutT06U*G%Q_jv2Aw@`KU_BH|E?&PgFa|@c*b)6QTs`7BTvMviL9(H-SvA^B+S1`|J zwK&VQ9@)`D+=x+p5~@xyPbV&y3!!5I?Y6S2mz?c792PfL8E5lGb{hY_RePU2dGekG zx}D^0mLH#6mSwWJ%jGi5vrjTizi{N65CYAZIm*FYavwtDgv~JqwIZCT?_=cspIO@g=vvXwl|HHSiZoZ}%r)%VS!rkw@ zyEag7(u^Yh;8A`?*lpWJ9h8`U{)0OD5@AWp(eMxg)?i&1T633p?4}QlS-J+LZLK}> z_G_)2))m1ud*P7TLkN!NTO-EA(gh7gx|Y!+ScGl6dVj!8gbYkzBiZKtXay=cPfu{D zKdo8U^|Onk2-oZNfR|=&Y>H4vKY+QrG%)r^WL|R}bjXC<>QtHIGrkCIJ}2jW>gLW! z4>7|!0u3q7OQcL?VD(^} zAv#U3trDXH958ar^9cU)&;3(=`PaW<-*=vW{&`+~^;PzLXHiA1hx`B(DW%B{Pj`3j zTmX5`eNsv(2fQ5qo2XZUWj)OVHKa1ZlV<6(_IAMsYa3_%dcF1pb+Es^Zp0YbwrlT3 z@!CJyV3uVe?~XGn9);emV!ZtFzd&Q+)mLBTYhU{s+qUt!fA+cAPo(r+9s2sVZEhpq zcy^x6m;ODb0684SHSr8Ihkyp)XgHmjBWru%D2i8eaKLU55Zm!62A zRd;)=HI}s8@ZfQDyu2DtREEGYO5VQjG?bhRX>rW(^m+yJMY?YLe7A@=5gQvM?dU7$ zq-^?$fG!COI;pZ0CGQu{CwWJmNimy-M4(}g0frnUM@HWFos<$Gjq?gI%50*dJIFZ~ zYDaqBu;73zXrPrgflG|B560PLEsc~G?#^dKy=fAR+H=J)ai+Ku5~Wl(J#XOoNRGe$ z{`aHh5K?1;4}ah{J9ZG{rI%i^y@&nCoH0gX3e?ib#ZAZ&o?d$!F+}eQ=~#a@Ka|3H zI+OE0NtM+GF%q=*A#}v(xkD%H77?!33ua91epHlUXp~Z?9w z{)3nQfFJ$%htyj5zz06WYp=aF#~{D+JAco_1fnA5o2a6X48_(OW%t&V^Z7iH*I2A( zAb1Byvx>@1=cqyoiFe+4hx_|`Z&&HMfY>cMz6z>e;`S z(`vnA>(u*ydK+F(WTg~nHbIhioAh4e-d1a^p~I>l@VgYY?K*pr4D-pGIao@|Bl-Ng zZ#ON#2hUIkK~8jhRRZ*|ZrMRkD;}by)((^!rIo{-0UC97i+X|^DPfdCpHvKiecLBR zz4a7F-{7R#Id^U%E_(Fn7`=zC=bV2k6Hr5kl-uQV#Ua=QEef#am@d zEfYKEoY4@tTrNm(7^=}bG~6cV5$zP|U1jQ(WLRrKv|H{DIfqGEtxgv=#z+unp-zv> zqgLH@U0v>Le!Rx-Y0n+owv##<^aR=cgu|IwLZC8^#KdyTo-hy%^N&R&GYQXVe5+s8UvWHZTkd4t(%{s(s#g}U+(~s2}}kTPbne2 zi(6DCs~aV&^#R?|<&mjlj4aDa45I-u%t#wO3$3|EF~FLWpU_$7GMl1?$lH3O#^?(1 zdj?9rkfadB50V=xGalzd~Dn7YVW*lEaQ!hrZX4lP0!*uLNO&;ZWGt&=)Etc zATfdpCA<3*V(RD7#SicJcC@A8u*Du89;RF_5@@Ber1N}yO(pO{8`|iBu%n;P&$;&y*P9+c*Z<4B8KPZ5vc}ZyMci9(?lEqCxbv*4J<;_R{o2c-3Tbqs zc1Rt1lXt)-8Xbvp-X_5i8u`X!baO(b)j}zO(C{#qaJZzZte&*}LEIwdWCnrOyHgSKTgPD=Y ztl8n^$w6o%%m~R*R!nKa(a|wBjE|HOM49()YOy)+A)UF@UYrPtH#6Qc zynTo%s@{%wqcHh`y3}SjeM;N5ad-F3L~;P3H#*FIq3Y3N=BzagU!bc|L6nDwD>mSG zZK&VPiHHjObw})&`0&*Cts-`~tCrg=Z!sOWziPkb)oSTCv%0P)`bY}%3es_ZJCq(( zDJs{XSH^m%Uhk_ zlZ-ubZtu=_vnFrbMlE^r1{EQ6Q$)LsmX<^iB_?lSqdEOPttaUCVwAE0fMQxE;}Z$w z-VV>)tTGecvMjDO9#^V;rl+z4Mgt%|pq>;DxdRwcsCKF6}qH|1LS^<+ormBa) zkdlX3D+AH#ybjxtg2D}pMuSTDi*BYS;BpQsR|5qcCJ-ED`ne7#;L5E=|I zrbr5j%hhj`$wA}-_xJaGcXcL?*37*+;bwtuuf+B4E!6KCr{k8UwTkr)vf&kSLthSd z=s!!*clYVjr&Rnx)3_)#xW-7vK^>0I#DkZ>(b(yC9Tx8autsg(RbET=lG@#G8C<}u zPMNurNJm*Aa9Y>SzHS;9g^n6)r<{%*Xl-6|sm-v~*!Qhd&OnT5X6u;{MS9kLOt**M zXU#Q+(TX?P;_HyT`$7a2M|vVy^YQqx^u~{x+gzO7?@80)_x}NIs)uO3`&Rq_0000< KMNUMnLSTZScQCpD literal 0 HcmV?d00001 diff --git a/textures/smart_light_panel_led_alarm.png b/textures/smart_light_panel_led_alarm.png new file mode 100644 index 0000000000000000000000000000000000000000..48a4233de1d85ff22644357978eb7073bf4c8183 GIT binary patch literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F3${@^GvDCf{D9B#o z>Fdh=h(kiyLVNnL8jt~!C9V-A&iT2ysd*&~&PAz-C8;S2<(VZJ3hti10pX2&;y^|E zo-U3d6}R4AFyv)m;9%bHaz_39gr=6|Q!lUpWf^`Ly^R3TP@tz{`I*U>!6Lx~B;e`l K=d#Wzp$P!qOf4+{ literal 0 HcmV?d00001 diff --git a/textures/smart_light_panel_led_supervisory.png b/textures/smart_light_panel_led_supervisory.png new file mode 100644 index 0000000000000000000000000000000000000000..285ebab7cd864defbf8aff6896ff855eb9d3e0c6 GIT binary patch literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F3${@^GvDCf{D9B#o z>Fdh=h(kiyl6iN=(~UqO$r9Iy66gHf+|;}h2Ir#G#FEq$h4Rdj3q literal 0 HcmV?d00001 diff --git a/textures/smart_light_panel_led_trouble.png b/textures/smart_light_panel_led_trouble.png new file mode 100644 index 0000000000000000000000000000000000000000..5196dde9bc48901b02c3541158e49c4fc31229dc GIT binary patch literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F3${@^GvDCf{D9B#o z>Fdh=h(kiyQhebi!!tl3$r9Iy66gHf+|;}h2Ir#G#FEq$h4Rdj3nUy90B^emvw*+&88DQeTQKql#%o47@lbS(% MPgg&ebxsLQ00k;8kpKVy literal 0 HcmV?d00001 diff --git a/textures/smart_light_panel_sides.png b/textures/smart_light_panel_sides.png new file mode 100644 index 0000000000000000000000000000000000000000..e9db9262640cfa6111fa53726b691f165193eb8e GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1|;Q0k92}K#X;^)j5kl})B`yz>5jgR3=A9l zx&I`x0{IHb9znhg3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!RzVb j7{YNq*}&NNKR*N08^*<(_BkE}N-%i3`njxgN@xNA3}GRi literal 0 HcmV?d00001 diff --git a/textures/smart_light_programmer.png b/textures/smart_light_programmer.png new file mode 100644 index 0000000000000000000000000000000000000000..4006e05554e785a140399b899cf86ee211a6f15c GIT binary patch literal 536 zcmV+z0_XjSP)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUzmPtfGR9M69m(NbZKoG`%g$4v_;-5q%BqRp!p3A|9Fy$5CG2|&o zcm?PiaDW&?k8@=}f^X}KHs%jdBAvppl zl~m7jU?~7QXI@Ic?_JdKXKs=X@;+-X1Lg!N_#>w2X{;AO)v$TF`%hN#D}P&oFGeL_s8OJ*y;n z`8hti_%8?1AglTd;3o=_BLKZ@JBst5HMO}~w%G~=s8($@UAHfzscD-hA^GK|LSdt2 z>jTg-%aLcCYz`6son0V=aMjVWgm$FB&9;1_9Pp|w{{kSF%Snsw241!0LI@1Qkhb^2 aUm+j5DtFqR(w+_g0000