497 lines
12 KiB
Lua
497 lines
12 KiB
Lua
local devices = require("automation:devices")
|
|
local utils = require("automation:utils")
|
|
local secrets = require("automation:secrets")
|
|
local helper = require("config.helper")
|
|
local presence = require("config.presence")
|
|
local windows = require("config.windows")
|
|
local ntfy = require("config.ntfy")
|
|
local light = require("config.light")
|
|
local battery = require("config.battery")
|
|
local hue_bridge = require("config.hue_bridge")
|
|
local hallway_automation = require("config.hallway_automation")
|
|
local debug = require("config.debug")
|
|
|
|
hue_bridge.setup()
|
|
|
|
print(_VERSION)
|
|
|
|
local host = utils.get_hostname()
|
|
print("Running @" .. host)
|
|
|
|
local kitchen_lights = devices.HueGroup.new({
|
|
identifier = "kitchen_lights",
|
|
ip = hue_bridge.ip,
|
|
login = hue_bridge.token,
|
|
group_id = 7,
|
|
scene_id = "7MJLG27RzeRAEVJ",
|
|
})
|
|
local living_lights = devices.HueGroup.new({
|
|
identifier = "living_lights",
|
|
ip = hue_bridge.ip,
|
|
login = hue_bridge.token,
|
|
group_id = 1,
|
|
scene_id = "SNZw7jUhQ3cXSjkj",
|
|
})
|
|
local living_lights_relax = devices.HueGroup.new({
|
|
identifier = "living_lights",
|
|
ip = hue_bridge.ip,
|
|
login = hue_bridge.token,
|
|
group_id = 1,
|
|
scene_id = "eRJ3fvGHCcb6yNw",
|
|
})
|
|
local hallway_top_light = devices.HueGroup.new({
|
|
identifier = "hallway_top_light",
|
|
ip = hue_bridge.ip,
|
|
login = hue_bridge.token,
|
|
group_id = 83,
|
|
scene_id = "QeufkFDICEHWeKJ7",
|
|
})
|
|
local hallway_bottom_lights = devices.HueGroup.new({
|
|
identifier = "hallway_bottom_lights",
|
|
ip = hue_bridge.ip,
|
|
login = hue_bridge.token,
|
|
group_id = 81,
|
|
scene_id = "3qWKxGVadXFFG4o",
|
|
})
|
|
hallway_automation.add_callback(function(on)
|
|
hallway_bottom_lights:set_on(on)
|
|
end)
|
|
local bedroom_lights = devices.HueGroup.new({
|
|
identifier = "bedroom_lights",
|
|
ip = hue_bridge.ip,
|
|
login = hue_bridge.token,
|
|
group_id = 3,
|
|
scene_id = "PvRs-lGD4VRytL9",
|
|
})
|
|
local bedroom_lights_relax = devices.HueGroup.new({
|
|
identifier = "bedroom_lights",
|
|
ip = hue_bridge.ip,
|
|
login = hue_bridge.token,
|
|
group_id = 3,
|
|
scene_id = "60tfTyR168v2csz",
|
|
})
|
|
|
|
local bedroom_air_filter = devices.AirFilter.new({
|
|
name = "Air Filter",
|
|
room = "Bedroom",
|
|
url = "http://10.0.0.103",
|
|
})
|
|
|
|
local function create_devs(mqtt_client)
|
|
local devs = {}
|
|
function devs:add(device)
|
|
table.insert(self, device)
|
|
end
|
|
|
|
devs:add(devices.HueSwitch.new({
|
|
name = "Switch",
|
|
room = "Living",
|
|
client = mqtt_client,
|
|
topic = helper.mqtt_z2m("living/switch"),
|
|
left_callback = function()
|
|
kitchen_lights:set_on(not kitchen_lights:on())
|
|
end,
|
|
right_callback = function()
|
|
living_lights:set_on(not living_lights:on())
|
|
end,
|
|
right_hold_callback = function()
|
|
living_lights_relax:set_on(true)
|
|
end,
|
|
battery_callback = battery.callback,
|
|
}))
|
|
|
|
devs:add(devices.WakeOnLAN.new({
|
|
name = "Zeus",
|
|
room = "Living Room",
|
|
topic = helper.mqtt_automation("appliance/living_room/zeus"),
|
|
client = mqtt_client,
|
|
mac_address = "30:9c:23:60:9c:13",
|
|
broadcast_ip = "10.0.3.255",
|
|
}))
|
|
|
|
local living_mixer = devices.OutletOnOff.new({
|
|
name = "Mixer",
|
|
room = "Living Room",
|
|
topic = helper.mqtt_z2m("living/mixer"),
|
|
client = mqtt_client,
|
|
})
|
|
presence.turn_off_when_away(living_mixer)
|
|
devs:add(living_mixer)
|
|
local living_speakers = devices.OutletOnOff.new({
|
|
name = "Speakers",
|
|
room = "Living Room",
|
|
topic = helper.mqtt_z2m("living/speakers"),
|
|
client = mqtt_client,
|
|
})
|
|
presence.turn_off_when_away(living_speakers)
|
|
devs:add(living_speakers)
|
|
|
|
devs:add(devices.IkeaRemote.new({
|
|
name = "Remote",
|
|
room = "Living Room",
|
|
client = mqtt_client,
|
|
topic = helper.mqtt_z2m("living/remote"),
|
|
single_button = true,
|
|
callback = function(_, on)
|
|
if on then
|
|
if living_mixer:on() then
|
|
living_mixer:set_on(false)
|
|
living_speakers:set_on(false)
|
|
else
|
|
living_mixer:set_on(true)
|
|
living_speakers:set_on(true)
|
|
end
|
|
else
|
|
if not living_mixer:on() then
|
|
living_mixer:set_on(true)
|
|
else
|
|
living_speakers:set_on(not living_speakers:on())
|
|
end
|
|
end
|
|
end,
|
|
battery_callback = battery.callback,
|
|
}))
|
|
|
|
--- @type OutletPower
|
|
local kettle = devices.OutletPower.new({
|
|
outlet_type = "Kettle",
|
|
name = "Kettle",
|
|
room = "Kitchen",
|
|
topic = helper.mqtt_z2m("kitchen/kettle"),
|
|
client = mqtt_client,
|
|
callback = helper.auto_off(),
|
|
})
|
|
presence.turn_off_when_away(kettle)
|
|
devs:add(kettle)
|
|
|
|
--- @param on boolean
|
|
local function set_kettle(_, on)
|
|
kettle:set_on(on)
|
|
end
|
|
|
|
devs:add(devices.IkeaRemote.new({
|
|
name = "Remote",
|
|
room = "Bedroom",
|
|
client = mqtt_client,
|
|
topic = helper.mqtt_z2m("bedroom/remote"),
|
|
single_button = true,
|
|
callback = set_kettle,
|
|
battery_callback = battery.callback,
|
|
}))
|
|
|
|
devs:add(devices.IkeaRemote.new({
|
|
name = "Remote",
|
|
room = "Kitchen",
|
|
client = mqtt_client,
|
|
topic = helper.mqtt_z2m("kitchen/remote"),
|
|
single_button = true,
|
|
callback = set_kettle,
|
|
battery_callback = battery.callback,
|
|
}))
|
|
|
|
local bathroom_light = devices.LightOnOff.new({
|
|
name = "Light",
|
|
room = "Bathroom",
|
|
topic = helper.mqtt_z2m("bathroom/light"),
|
|
client = mqtt_client,
|
|
callback = helper.off_timeout(debug.debug_mode and 60 or 45 * 60),
|
|
})
|
|
devs:add(bathroom_light)
|
|
|
|
devs:add(devices.Washer.new({
|
|
identifier = "bathroom_washer",
|
|
topic = helper.mqtt_z2m("bathroom/washer"),
|
|
client = mqtt_client,
|
|
threshold = 1,
|
|
done_callback = function()
|
|
ntfy.device:send_notification({
|
|
title = "Laundy is done",
|
|
message = "Don't forget to hang it!",
|
|
tags = { "womans_clothes" },
|
|
priority = "high",
|
|
})
|
|
end,
|
|
}))
|
|
|
|
devs:add(devices.OutletOnOff.new({
|
|
name = "Charger",
|
|
room = "Workbench",
|
|
topic = helper.mqtt_z2m("workbench/charger"),
|
|
client = mqtt_client,
|
|
callback = helper.off_timeout(debug.debug_mode and 5 or 20 * 3600),
|
|
}))
|
|
|
|
local workbench_outlet = devices.OutletOnOff.new({
|
|
name = "Outlet",
|
|
room = "Workbench",
|
|
topic = helper.mqtt_z2m("workbench/outlet"),
|
|
client = mqtt_client,
|
|
})
|
|
presence.turn_off_when_away(workbench_outlet)
|
|
devs:add(workbench_outlet)
|
|
|
|
local workbench_light = devices.LightColorTemperature.new({
|
|
name = "Light",
|
|
room = "Workbench",
|
|
topic = helper.mqtt_z2m("workbench/light"),
|
|
client = mqtt_client,
|
|
})
|
|
presence.turn_off_when_away(workbench_light)
|
|
devs:add(workbench_light)
|
|
|
|
local delay_color_temp = utils.Timeout.new()
|
|
devs:add(devices.IkeaRemote.new({
|
|
name = "Remote",
|
|
room = "Workbench",
|
|
client = mqtt_client,
|
|
topic = helper.mqtt_z2m("workbench/remote"),
|
|
callback = function(_, on)
|
|
delay_color_temp:cancel()
|
|
if on then
|
|
workbench_light:set_brightness(82)
|
|
-- NOTE: This light does NOT support changing both the brightness and color
|
|
-- temperature at the same time, so we first change the brightness and once
|
|
-- that is complete we change the color temperature, as that is less likely
|
|
-- to have to actually change.
|
|
delay_color_temp:start(0.5, function()
|
|
workbench_light:set_color_temperature(3333)
|
|
end)
|
|
else
|
|
workbench_light:set_on(false)
|
|
end
|
|
end,
|
|
battery_callback = battery.callback,
|
|
}))
|
|
|
|
devs:add(devices.HueSwitch.new({
|
|
name = "SwitchBottom",
|
|
room = "Hallway",
|
|
client = mqtt_client,
|
|
topic = helper.mqtt_z2m("hallway/switchbottom"),
|
|
left_callback = function()
|
|
hallway_top_light:set_on(not hallway_top_light:on())
|
|
end,
|
|
battery_callback = battery.callback,
|
|
}))
|
|
devs:add(devices.HueSwitch.new({
|
|
name = "SwitchTop",
|
|
room = "Hallway",
|
|
client = mqtt_client,
|
|
topic = helper.mqtt_z2m("hallway/switchtop"),
|
|
left_callback = function()
|
|
hallway_top_light:set_on(not hallway_top_light:on())
|
|
end,
|
|
battery_callback = battery.callback,
|
|
}))
|
|
|
|
local hallway_storage = devices.LightBrightness.new({
|
|
name = "Storage",
|
|
room = "Hallway",
|
|
topic = helper.mqtt_z2m("hallway/storage"),
|
|
client = mqtt_client,
|
|
callback = hallway_automation.light_callback,
|
|
})
|
|
presence.turn_off_when_away(hallway_storage)
|
|
devs:add(hallway_storage)
|
|
hallway_automation.add_callback(function(on)
|
|
if on then
|
|
hallway_storage:set_brightness(80)
|
|
else
|
|
hallway_storage:set_on(false)
|
|
end
|
|
end)
|
|
|
|
devs:add(devices.IkeaRemote.new({
|
|
name = "Remote",
|
|
room = "Hallway",
|
|
client = mqtt_client,
|
|
topic = helper.mqtt_z2m("hallway/remote"),
|
|
callback = hallway_automation.switch_callback,
|
|
battery_callback = battery.callback,
|
|
}))
|
|
|
|
---@param duration number
|
|
---@return fun(_, open: boolean)
|
|
local function frontdoor_presence(duration)
|
|
local timeout = utils.Timeout.new()
|
|
|
|
return function(_, open)
|
|
if open then
|
|
timeout:cancel()
|
|
|
|
if presence.device and not presence.device:overall_presence() then
|
|
mqtt_client:send_message(helper.mqtt_automation("presence/contact/frontdoor"), {
|
|
state = true,
|
|
updated = utils.get_epoch(),
|
|
})
|
|
end
|
|
else
|
|
timeout:start(duration, function()
|
|
mqtt_client:send_message(helper.mqtt_automation("presence/contact/frontdoor"), nil)
|
|
end)
|
|
end
|
|
end
|
|
end
|
|
local hallway_frontdoor = devices.ContactSensor.new({
|
|
name = "Frontdoor",
|
|
room = "Hallway",
|
|
sensor_type = "Door",
|
|
topic = helper.mqtt_z2m("hallway/frontdoor"),
|
|
client = mqtt_client,
|
|
callback = {
|
|
frontdoor_presence(debug.debug_mode and 10 or 15 * 60),
|
|
hallway_automation.door_callback,
|
|
},
|
|
battery_callback = battery.callback,
|
|
})
|
|
devs:add(hallway_frontdoor)
|
|
windows.add(hallway_frontdoor)
|
|
hallway_automation.set_door(hallway_frontdoor)
|
|
|
|
local hallway_trash = devices.ContactSensor.new({
|
|
name = "Trash",
|
|
room = "Hallway",
|
|
sensor_type = "Drawer",
|
|
topic = helper.mqtt_z2m("hallway/trash"),
|
|
client = mqtt_client,
|
|
callback = hallway_automation.trash_callback,
|
|
battery_callback = battery.callback,
|
|
})
|
|
devs:add(hallway_trash)
|
|
hallway_automation.set_trash(hallway_trash)
|
|
|
|
local guest_light = devices.LightOnOff.new({
|
|
name = "Light",
|
|
room = "Guest Room",
|
|
topic = helper.mqtt_z2m("guest/light"),
|
|
client = mqtt_client,
|
|
})
|
|
presence.turn_off_when_away(guest_light)
|
|
devs:add(guest_light)
|
|
|
|
devs:add(devices.HueSwitch.new({
|
|
name = "Switch",
|
|
room = "Bedroom",
|
|
client = mqtt_client,
|
|
topic = helper.mqtt_z2m("bedroom/switch"),
|
|
left_callback = function()
|
|
bedroom_lights:set_on(not bedroom_lights:on())
|
|
end,
|
|
left_hold_callback = function()
|
|
bedroom_lights_relax:set_on(true)
|
|
end,
|
|
battery_callback = battery.callback,
|
|
}))
|
|
|
|
local balcony = devices.ContactSensor.new({
|
|
name = "Balcony",
|
|
room = "Living Room",
|
|
sensor_type = "Door",
|
|
topic = helper.mqtt_z2m("living/balcony"),
|
|
client = mqtt_client,
|
|
battery_callback = battery.callback,
|
|
})
|
|
devs:add(balcony)
|
|
windows.add(balcony)
|
|
local living_window = devices.ContactSensor.new({
|
|
name = "Window",
|
|
room = "Living Room",
|
|
topic = helper.mqtt_z2m("living/window"),
|
|
client = mqtt_client,
|
|
battery_callback = battery.callback,
|
|
})
|
|
devs:add(living_window)
|
|
windows.add(living_window)
|
|
local bedroom_window = devices.ContactSensor.new({
|
|
name = "Window",
|
|
room = "Bedroom",
|
|
topic = helper.mqtt_z2m("bedroom/window"),
|
|
client = mqtt_client,
|
|
battery_callback = battery.callback,
|
|
})
|
|
devs:add(bedroom_window)
|
|
windows.add(bedroom_window)
|
|
local guest_window = devices.ContactSensor.new({
|
|
name = "Window",
|
|
room = "Guest Room",
|
|
topic = helper.mqtt_z2m("guest/window"),
|
|
client = mqtt_client,
|
|
battery_callback = battery.callback,
|
|
})
|
|
devs:add(guest_window)
|
|
windows.add(guest_window)
|
|
|
|
local storage_light = devices.LightBrightness.new({
|
|
name = "Light",
|
|
room = "Storage",
|
|
topic = helper.mqtt_z2m("storage/light"),
|
|
client = mqtt_client,
|
|
})
|
|
presence.turn_off_when_away(storage_light)
|
|
devs:add(storage_light)
|
|
|
|
devs:add(devices.ContactSensor.new({
|
|
name = "Door",
|
|
room = "Storage",
|
|
sensor_type = "Door",
|
|
topic = helper.mqtt_z2m("storage/door"),
|
|
client = mqtt_client,
|
|
callback = function(_, open)
|
|
if open then
|
|
storage_light:set_brightness(100)
|
|
else
|
|
storage_light:set_on(false)
|
|
end
|
|
end,
|
|
battery_callback = battery.callback,
|
|
}))
|
|
|
|
devs.add = nil
|
|
|
|
return devs
|
|
end
|
|
|
|
--- @type MqttConfig
|
|
local mqtt_config = {
|
|
host = ((host == "zeus" or host == "hephaestus") and "olympus.lan.huizinga.dev") or "mosquitto",
|
|
port = 8883,
|
|
client_name = "automation-" .. host,
|
|
username = "mqtt",
|
|
password = secrets.mqtt_password,
|
|
tls = host == "zeus" or host == "hephaestus",
|
|
}
|
|
|
|
---@type Config
|
|
return {
|
|
fulfillment = {
|
|
openid_url = "https://login.huizinga.dev/api/oidc",
|
|
},
|
|
mqtt = mqtt_config,
|
|
devices = {
|
|
create_devs,
|
|
ntfy.device,
|
|
presence.setup,
|
|
light.setup,
|
|
hue_bridge.setup,
|
|
debug.setup,
|
|
windows.setup,
|
|
kitchen_lights,
|
|
living_lights,
|
|
living_lights_relax,
|
|
hallway_top_light,
|
|
hallway_bottom_lights,
|
|
bedroom_lights,
|
|
bedroom_lights_relax,
|
|
bedroom_air_filter,
|
|
},
|
|
schedule = {
|
|
["0 0 19 * * *"] = function()
|
|
bedroom_air_filter:set_on(true)
|
|
end,
|
|
["0 0 20 * * *"] = function()
|
|
bedroom_air_filter:set_on(false)
|
|
end,
|
|
["0 0 21 */1 * *"] = battery.notify_low_battery,
|
|
},
|
|
}
|