working implementation

This commit is contained in:
Emil Lerch 2025-12-09 21:13:52 -08:00
parent 802b9e766b
commit 100d49b4c7
Signed by: lobo
GPG key ID: A7B62D657EF764F8

View file

@ -228,37 +228,55 @@ fn getDevices(allocator: std.mem.Allocator, id_token: []const u8, username: []co
}
/// Starts recirculation for the specified device with given duration
fn startRecirculation(allocator: std.mem.Allocator, id_token: []const u8, serial_number: []const u8, duration_minutes: u32) !void {
fn setRecirculation(allocator: std.mem.Allocator, id_token: []const u8, thing_name: []const u8, duration_minutes: ?u32) !void {
var client = http.Client{ .allocator = allocator };
defer client.deinit();
const url = try std.fmt.allocPrint(allocator,
\\{s}/{s}/shadow
, .{ shadow_api_url, serial_number });
, .{ shadow_api_url, thing_name });
defer allocator.free(url);
const body = try std.fmt.allocPrint(allocator,
\\{{"recirculation_duration":{d},"set_recirculation_enabled":true}}
, .{duration_minutes});
const body = if (duration_minutes) |min|
try std.fmt.allocPrint(allocator,
\\{{"recirculation_duration":"{d}","set_recirculation_enabled":true}}
, .{min})
else
try allocator.dupe(u8,
\\{"set_recirculation_enabled":false}
);
defer allocator.free(body);
// std.debug.print("DEBUG: PATCH URL: {s}\n", .{url});
// std.debug.print("DEBUG: PATCH Body: {s}\n", .{body});
const uri = try std.Uri.parse(url);
const auth_header = try std.fmt.allocPrint(allocator,
\\Bearer {s}
, .{id_token});
defer allocator.free(auth_header);
var response_buf: [4096]u8 = undefined;
var writer = std.Io.Writer.fixed(&response_buf);
const result = try client.fetch(.{
.location = .{ .uri = uri },
.method = .PATCH,
.payload = body,
.response_writer = &writer,
.extra_headers = &.{
.{ .name = "Authorization", .value = auth_header },
.{ .name = "Content-Type", .value = "application/json" },
},
});
if (result.status != .ok) return error.StartRecirculationFailed;
const response_body = response_buf[0..writer.end];
// std.debug.print("DEBUG: PATCH Response Status: {}\n", .{result.status});
// std.debug.print("DEBUG: PATCH Response Body: {s}\n", .{response_body});
if (result.status != .ok) {
std.debug.print("PATCH failed - Status: {}, Body: {s}\n", .{ result.status, response_body });
return error.StartRecirculationFailed;
}
}
const DeviceShadow = struct {
@ -372,6 +390,10 @@ pub fn main() !void {
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);
const debug_mode = args.len > 1 and std.mem.eql(u8, args[1], "--debug");
var stdout_buffer: [1024]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
const stdout = &stdout_writer.interface;
@ -393,6 +415,12 @@ pub fn main() !void {
defer allocator.free(auth.id_token);
defer allocator.free(auth.user_uuid);
try stdout.print("✓ User UUID: {s}\n\n", .{auth.user_uuid});
if (debug_mode) {
try stdout.print("\n=== DEBUG MODE ===\n", .{});
try stdout.print("IdToken:\n{s}\n\n", .{auth.id_token});
}
try stdout.print("📱 Fetching devices...\n", .{});
try stdout.flush();
var result = try getDevices(
@ -422,54 +450,96 @@ pub fn main() !void {
1 => {
const device = result.devices[0];
if (device.serial_id) |sid| {
try stdout.print("🔍 Checking recirculation status for {?s}...\n", .{device.device_name});
try stdout.flush();
var status = try getRecirculationStatus(allocator, auth.id_token, sid);
defer status.deinit();
try stdout.print("\n{f}", .{status});
const recirc_enabled = status.recirculation_enabled orelse false;
if (recirc_enabled) {
try stdout.print("\n✓ Recirculation is already active\n", .{});
} else {
try stdout.print("\n🚿 Starting 15-minute recirculation...\n", .{});
try stdout.flush();
startRecirculation(
allocator,
auth.id_token,
sid,
15,
) catch |e| {
try stderr.print("❌ Failed to start recirculation\n", .{});
try stderr.flush();
return e;
};
try stdout.print("✓ Recirculation command sent\n", .{});
try stdout.print("⏳ Waiting 20 seconds for device to respond...\n", .{});
try stdout.flush();
std.Thread.sleep(20 * std.time.ns_per_s);
var post_command_state = try getRecirculationStatus(
allocator,
auth.id_token,
sid,
);
defer post_command_state.deinit();
try stdout.print("\n{f}", .{post_command_state});
}
try stdout.flush();
} else {
try stderr.print("❌ No serial_id found for device\n", .{});
if (device.serial_id == null or
device.thing_name == null)
{
try stderr.print("❌ thing_name and serial_id both required\n", .{});
try stderr.flush();
return error.NoSerialId;
return error.SerialIdAndThingNameRequired;
}
const sid = device.serial_id.?;
const tn = device.thing_name.?;
try stdout.print("🔍 Checking recirculation status for {?s}...\n", .{device.device_name});
try stdout.flush();
var status = try getRecirculationStatus(allocator, auth.id_token, sid);
defer status.deinit();
try stdout.print("\n{f}", .{status});
if (debug_mode) {
try stdout.print("\n=== CURL COMMANDS ===\n", .{});
try stdout.print("Reset recirculation:\n", .{});
try stdout.print("curl -X PATCH '{s}/{s}/shadow' \\\n", .{ shadow_api_url, tn });
try stdout.print(" -H 'Authorization: Bearer {s}' \\\n", .{auth.id_token});
try stdout.print(" -H 'Content-Type: application/json' \\\n", .{});
try stdout.print(" -d '{{\"set_recirculation_enabled\":false}}'\n\n", .{});
try stdout.print("Start recirculation:\n", .{});
try stdout.print("curl -X PATCH '{s}/{s}/shadow' \\\n", .{ shadow_api_url, tn });
try stdout.print(" -H 'Authorization: Bearer {s}' \\\n", .{auth.id_token});
try stdout.print(" -H 'Content-Type: application/json' \\\n", .{});
try stdout.print(" -d '{{\"recirculation_duration\":15,\"set_recirculation_enabled\":true}}'\n\n", .{});
try stdout.flush();
return;
}
const recirc_enabled = status.recirculation_enabled orelse false;
if (recirc_enabled) {
try stdout.print("\n✓ Recirculation is already active\n", .{});
} else {
try stdout.print("\n🚿 Starting 15-minute recirculation...\n", .{});
try stdout.flush();
if (status.set_recirculation_enabled) |en| {
if (en) {
try stdout.print("⚠️ set_recirculation_enabled is already true, resetting first...\n", .{});
try stdout.flush();
// The shadow state doesn't seem to update. The react native
// application doesn't wait, it just yolo's here, so
// we'll do the same
try setRecirculation(
allocator,
auth.id_token,
sid,
null,
);
try stdout.print("⏳ Waiting 3 seconds...\n", .{});
try stdout.flush();
std.Thread.sleep(3 * std.time.ns_per_s);
}
}
setRecirculation(
allocator,
auth.id_token,
tn,
15,
) catch |e| {
try stderr.print("❌ Failed to start recirculation\n", .{});
try stderr.flush();
return e;
};
try stdout.print("✓ Recirculation command sent\n", .{});
try stdout.print("⏳ Waiting 20 seconds for device to respond...\n", .{});
try stdout.flush();
std.Thread.sleep(20 * std.time.ns_per_s);
var post_command_state = try getRecirculationStatus(
allocator,
auth.id_token,
sid,
);
defer post_command_state.deinit();
try stdout.print("\n{f}", .{post_command_state});
}
try stdout.flush();
},
else => {
try stderr.print("❌ More than one device found for user, not currently equipped to handle this\n", .{});