From e6be04283d082aa151b3d7d50bd6f2c37cd2eca9 Mon Sep 17 00:00:00 2001 From: Mia Bennett Date: Wed, 12 Feb 2025 13:11:28 +0930 Subject: [PATCH 1/5] feat(room_at_capacity_mailer): notify when people_count equals or exceeds capacity --- drivers/place/room_at_capacity_mailer.cr | 137 +++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 drivers/place/room_at_capacity_mailer.cr diff --git a/drivers/place/room_at_capacity_mailer.cr b/drivers/place/room_at_capacity_mailer.cr new file mode 100644 index 0000000000..91a84a0d76 --- /dev/null +++ b/drivers/place/room_at_capacity_mailer.cr @@ -0,0 +1,137 @@ +require "placeos-driver" +require "placeos-driver/interface/mailer" +require "placeos-driver/interface/mailer_templates" + +class Place::RoomAtCapacityMailer < PlaceOS::Driver + include PlaceOS::Driver::Interface::MailerTemplates + + descriptive_name "PlaceOS Room at capacity mailer" + generic_name :RoomAtCapacityMailer + description %(notifies when a room is at capacity) + + default_settings({ + notify_email: ["concierge@place.com"], + debounce_time_minutes: 60, # the time to wait before sending another email + email_template: "room_at_capacity", + }) + + accessor staff_api : StaffAPI_1 + accessor locations : LocationServices_1 + + def mailer + system.implementing(Interface::Mailer)[0] + end + + getter building_id : String do + locations.building_id.get.as_s + end + + # Grabs the list of systems in the building + getter systems : Hash(String, Array(String)) do + staff_api.systems_in_building(building_id).get.as_h.transform_values(&.as_a.map(&.as_s)) + end + + def on_load + on_update + end + + @notify_email : Array(String) = [] of String + @debounce_time_minutes : Int32 = 60 + @last_email_sent : Hash(String, Time) = {} of String => Time + + @email_template : String = "room_at_capacity" + + def on_update + @building_id = nil + @systems = nil + + @notify_email = setting?(Array(String), :notify_email) || [] of String + @debounce_time_minutes = setting?(Int32, :debounce_time_minutes) || 60 + + @email_template = setting?(String, :email_template) || "room_at_capacity" + + schedule.clear + + schedule.every(20.seconds) { check_capacity } + end + + def check_capacity + systems.each do |level_id, system_ids| + system_ids.each do |system_id| + sys = system(system_id) + next unless sys.exists?("Bookings", 1) + next unless sys.capacity > 0 + + if people_count = sys.get("Bookings", 1).status?(Int32, "people_count") + logger.debug { "people count for #{system_id}: #{people_count}" } + if people_count >= sys.capacity + send_email( + sys.capacity, + people_count, + system_id, + sys.name, + sys.display_name, + sys.description, + sys.email, + ) + end + end + end + end + end + + @[Security(Level::Support)] + def send_email( + capacity : Int32, + people_count : Int32, + system_id : String, + name : String? = nil, + display_name : String? = nil, + description : String? = nil, + system_email : String? = nil, + ) + if (last = @last_email_sent[system_id]?) && Time.utc - last < @debounce_time_minutes.minutes + logger.debug { "skipping email for #{system_id} due to debounce timer" } + return + end + + args = { + system_id: system_id, + name: name, + display_name: display_name, + description: description, + system_email: system_email, + capacity: capacity, + people_count: people_count, + } + + begin + mailer.send_template( + to: @notify_email, + template: {"room_at_capacity", @email_template}, + args: args) + @last_email_sent[system_id] = Time.utc + rescue error + logger.warn(exception: error) { "failed to send at capacity email for zone #{system_id}" } + end + end + + def template_fields : Array(TemplateFields) + [ + TemplateFields.new( + trigger: {"room_at_capacity", @email_template}, + name: "Room at capacity", + description: "Notification when a room is at capacity", + fields: [ + {name: "system_id", description: "Identifier of the room/system"}, + {name: "name", description: "Room name"}, + {name: "display_name", description: "Room display name"}, + {name: "description", description: "Room description"}, + {name: "system_email", description: "System/room email address"}, + {name: "capacity", description: "Capacity of the room"}, + {name: "people_count", description: "Number of people in the room"}, + ] + ), + ] + end +end From 1ba203566815fe0b63e9f3efda69e8c6dced3352 Mon Sep 17 00:00:00 2001 From: Mia Bennett Date: Wed, 12 Feb 2025 13:12:32 +0930 Subject: [PATCH 2/5] refactor(at_capacity_mailer): add description and make email template configurable --- drivers/place/at_capacity_mailer.cr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/place/at_capacity_mailer.cr b/drivers/place/at_capacity_mailer.cr index 0ffd2f2192..aa9beb135a 100644 --- a/drivers/place/at_capacity_mailer.cr +++ b/drivers/place/at_capacity_mailer.cr @@ -8,7 +8,7 @@ class Place::AtCapacityMailer < PlaceOS::Driver descriptive_name "PlaceOS At Capacity Mailer" generic_name :AtCapacityMailer - description %() + description %(sends a notification when the specified type is at capacity) default_settings({ timezone: "Australia/Sydney", @@ -62,6 +62,7 @@ class Place::AtCapacityMailer < PlaceOS::Driver @time_window_hours = setting?(Int32, :time_window_hours) || 1 @debounce_time_minutes = setting?(Int32, :debounce_time_minutes) || 60 + @email_template = setting?(String, :email_template) || "at_capacity" @unique_templates = setting?(Bool, :unique_templates) || false @template_suffix = @unique_templates ? "_#{@booking_type}" : "" @template_fields_suffix = @unique_templates ? " (#{@booking_type})" : "" From 81c954d25c6750b531461319cef986222462ad52 Mon Sep 17 00:00:00 2001 From: Mia Bennett Date: Wed, 12 Feb 2025 13:39:33 +0930 Subject: [PATCH 3/5] feat(room_at_capacity): configurable check interval --- drivers/place/room_at_capacity_mailer.cr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/place/room_at_capacity_mailer.cr b/drivers/place/room_at_capacity_mailer.cr index 91a84a0d76..8db71cacd4 100644 --- a/drivers/place/room_at_capacity_mailer.cr +++ b/drivers/place/room_at_capacity_mailer.cr @@ -13,6 +13,7 @@ class Place::RoomAtCapacityMailer < PlaceOS::Driver notify_email: ["concierge@place.com"], debounce_time_minutes: 60, # the time to wait before sending another email email_template: "room_at_capacity", + check_every_minutes: 5, # the frequency to check rooms }) accessor staff_api : StaffAPI_1 @@ -47,12 +48,11 @@ class Place::RoomAtCapacityMailer < PlaceOS::Driver @notify_email = setting?(Array(String), :notify_email) || [] of String @debounce_time_minutes = setting?(Int32, :debounce_time_minutes) || 60 - @email_template = setting?(String, :email_template) || "room_at_capacity" + period = setting?(Int32, :check_every_minutes) || 5 schedule.clear - - schedule.every(20.seconds) { check_capacity } + schedule.every(period.minutes) { check_capacity } end def check_capacity From 60d8c15361863be0a4df018ae8d6869a901b5b73 Mon Sep 17 00:00:00 2001 From: Mia Bennett Date: Wed, 12 Feb 2025 14:06:36 +0930 Subject: [PATCH 4/5] feat(room_at_capacity_mailer): only notify on multiple over capacity detection --- drivers/place/room_at_capacity_mailer.cr | 25 +++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/place/room_at_capacity_mailer.cr b/drivers/place/room_at_capacity_mailer.cr index 8db71cacd4..1957556bc7 100644 --- a/drivers/place/room_at_capacity_mailer.cr +++ b/drivers/place/room_at_capacity_mailer.cr @@ -10,10 +10,11 @@ class Place::RoomAtCapacityMailer < PlaceOS::Driver description %(notifies when a room is at capacity) default_settings({ - notify_email: ["concierge@place.com"], - debounce_time_minutes: 60, # the time to wait before sending another email - email_template: "room_at_capacity", - check_every_minutes: 5, # the frequency to check rooms + notify_email: ["concierge@place.com"], + email_template: "room_at_capacity", + debounce_time_minutes: 60, # the time to wait before sending another email + check_every_minutes: 5, # the frequency to check rooms + over_capactity_detected_count: 2, # the number of times over capacity before sending an email }) accessor staff_api : StaffAPI_1 @@ -37,18 +38,21 @@ class Place::RoomAtCapacityMailer < PlaceOS::Driver end @notify_email : Array(String) = [] of String + @email_template : String = "room_at_capacity" @debounce_time_minutes : Int32 = 60 - @last_email_sent : Hash(String, Time) = {} of String => Time + @over_capactity_detected_count : Int32 = 2 - @email_template : String = "room_at_capacity" + @last_email_sent : Hash(String, Time) = {} of String => Time + @over_capacity : Hash(String, Int32) = {} of String => Int32 def on_update @building_id = nil @systems = nil @notify_email = setting?(Array(String), :notify_email) || [] of String - @debounce_time_minutes = setting?(Int32, :debounce_time_minutes) || 60 @email_template = setting?(String, :email_template) || "room_at_capacity" + @debounce_time_minutes = setting?(Int32, :debounce_time_minutes) || 60 + @over_capactity_detected_count = setting?(Int32, :over_capactity_detected_count) || 2 period = setting?(Int32, :check_every_minutes) || 5 schedule.clear @@ -64,7 +68,14 @@ class Place::RoomAtCapacityMailer < PlaceOS::Driver if people_count = sys.get("Bookings", 1).status?(Int32, "people_count") logger.debug { "people count for #{system_id}: #{people_count}" } + if people_count >= sys.capacity + @over_capacity[system_id] = @over_capacity[system_id]? ? @over_capacity[system_id] + 1 : 1 + else + @over_capacity[system_id] = 0 + end + + if (over = @over_capacity[system_id]?) && over == @over_capactity_detected_count send_email( sys.capacity, people_count, From fbd4b4021953b12b6143b0123f523db80816165a Mon Sep 17 00:00:00 2001 From: Mia Bennett Date: Wed, 12 Feb 2025 15:21:11 +0930 Subject: [PATCH 5/5] refactor(room_at_capacity_mailer): over capacity counter --- drivers/place/room_at_capacity_mailer.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/place/room_at_capacity_mailer.cr b/drivers/place/room_at_capacity_mailer.cr index 1957556bc7..ac8f0b6de3 100644 --- a/drivers/place/room_at_capacity_mailer.cr +++ b/drivers/place/room_at_capacity_mailer.cr @@ -43,7 +43,7 @@ class Place::RoomAtCapacityMailer < PlaceOS::Driver @over_capactity_detected_count : Int32 = 2 @last_email_sent : Hash(String, Time) = {} of String => Time - @over_capacity : Hash(String, Int32) = {} of String => Int32 + @over_capacity : Hash(String, Int32) = Hash(String, Int32).new { |hash, sys_id| hash[sys_id] = 0 } def on_update @building_id = nil @@ -70,7 +70,7 @@ class Place::RoomAtCapacityMailer < PlaceOS::Driver logger.debug { "people count for #{system_id}: #{people_count}" } if people_count >= sys.capacity - @over_capacity[system_id] = @over_capacity[system_id]? ? @over_capacity[system_id] + 1 : 1 + @over_capacity[system_id] += 1 else @over_capacity[system_id] = 0 end