From cba06ba30e742afd7c448bba77f27d8440c12774 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Tue, 9 Dec 2025 13:28:58 -0800 Subject: [PATCH] move parsing into the getDevices function --- src/main.zig | 207 +++++++++++++++++++++++++++++---------------------- 1 file changed, 120 insertions(+), 87 deletions(-) diff --git a/src/main.zig b/src/main.zig index 04987ed..bb4ed3d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -104,8 +104,73 @@ fn authenticate(allocator: std.mem.Allocator, username: []const u8, password: [] return .{ .id_token = id_token_copy, .user_uuid = user_uuid_copy }; } +const Device = struct { + id: ?[]const u8, + thing_name: ?[]const u8, + device_name: ?[]const u8, + dsn: ?[]const u8, + model: ?[]const u8, + serial_id: ?[]const u8, +}; +const DeviceList = struct { + json_data: json.Parsed(json.Value), + devices: []Device, + + pub fn deinit(self: *DeviceList) void { + self.json_data.deinit(); + } +}; + +fn stringFromJson(obj: std.json.Value, field: []const u8) ?[]const u8 { + if (obj != .object) return null; + if (obj.object.get(field)) |v| { + if (v != .string) return null; + return v.string; + } + return null; +} +fn parseDeviceJson(parsed: json.Parsed(json.Value)) !DeviceList { + if (parsed.value.object.get("data")) |data| { + if (data.object.get("getUserByEmail")) |user_data| { + if (user_data.object.get("items")) |items| { + if (items.array.items.len > 0) { + if (items.array.items[0].object.get("devices")) |devices_obj| { + if (devices_obj.object.get("items")) |devices_json| { + const device_array = try parsed.arena.allocator().alloc(Device, devices_json.array.items.len); + + for (devices_json.array.items, 0..) |device_json, i| { + const id = stringFromJson(device_json, "id"); + const thing_name = stringFromJson(device_json, "thing_name"); + const device_name = stringFromJson(device_json, "device_name"); + const dsn = stringFromJson(device_json, "dsn"); + const model = stringFromJson(device_json, "model"); + const serial_id = if (device_json.object.get("info")) |info| stringFromJson(info, "serial_id") else null; + + device_array[i] = .{ + .id = id, + .thing_name = thing_name, + .device_name = device_name, + .dsn = dsn, + .model = model, + .serial_id = serial_id, + }; + } + + return .{ + .json_data = parsed, + .devices = device_array, + }; + } + } + } + } + } + } + return error.NoDevicesFound; +} + /// Fetches device list from AppSync GraphQL API for the given user -fn getDevices(allocator: std.mem.Allocator, id_token: []const u8, username: []const u8) !json.Parsed(json.Value) { +fn getDevices(allocator: std.mem.Allocator, id_token: []const u8, username: []const u8) !DeviceList { var client = http.Client{ .allocator = allocator }; defer client.deinit(); @@ -158,7 +223,8 @@ fn getDevices(allocator: std.mem.Allocator, id_token: []const u8, username: []co }); const response_body = response_buf[0..writer.end]; - return try json.parseFromSlice(json.Value, allocator, response_body, .{}); + const parsed = try json.parseFromSlice(json.Value, allocator, response_body, .{}); + return try parseDeviceJson(parsed); } /// Starts recirculation for the specified device with given duration @@ -280,106 +346,73 @@ pub fn main() !void { try stdout.print("āœ“ User UUID: {s}\n\n", .{auth.user_uuid}); try stdout.print("šŸ“± Fetching devices...\n", .{}); try stdout.flush(); - const result = try getDevices( + var result = try getDevices( allocator, auth.id_token, creds.username, ); defer result.deinit(); - if (result.value.object.get("data")) |data| { - if (data.object.get("getUserByEmail")) |user_data| { - if (user_data.object.get("items")) |items| { - if (items.array.items.len > 0) { - if (items.array.items[0].object.get("devices")) |devices_obj| { - if (devices_obj.object.get("items")) |devices| { - try stdout.print("\nāœ“ Found {d} device(s):\n\n", .{devices.array.items.len}); + try stdout.print("\nāœ“ Found {d} device(s):\n\n", .{result.devices.len}); - for (devices.array.items) |device| { - const device_name = if (device.object.get("device_name")) |n| if (n == .string) n.string else "Unnamed" else "Unnamed"; - const thing_name = if (device.object.get("thing_name")) |n| if (n == .string) n.string else "N/A" else "N/A"; - const dsn = if (device.object.get("dsn")) |n| if (n == .string) n.string else "N/A" else "N/A"; - const model = if (device.object.get("model")) |m| if (m == .string) m.string else "N/A" else "N/A"; - const serial_id = if (device.object.get("info")) |info| blk: { - if (info.object.get("serial_id")) |sid| { - if (sid == .string) break :blk sid.string else break :blk "N/A"; - } else break :blk "N/A"; - } else "N/A"; + for (result.devices) |device| { + try stdout.print(" • {?s}\n", .{device.device_name}); + try stdout.print(" Thing Name: {?s}\n", .{device.thing_name}); + try stdout.print(" DSN: {?s}\n", .{device.dsn}); + try stdout.print(" Serial ID: {?s}\n", .{device.serial_id}); + try stdout.print(" Model: {?s}\n\n", .{device.model}); + } + try stdout.flush(); - try stdout.print(" • {s}\n", .{device_name}); - try stdout.print(" Thing Name: {s}\n", .{thing_name}); - try stdout.print(" DSN: {s}\n", .{dsn}); - try stdout.print(" Serial ID: {s}\n", .{serial_id}); - try stdout.print(" Model: {s}\n\n", .{model}); - } - try stdout.flush(); + if (result.devices.len > 0) { + const device = result.devices[0]; - if (devices.array.items.len > 0) { - const device = devices.array.items[0]; - const serial_id = if (device.object.get("info")) |info| blk: { - if (info.object.get("serial_id")) |sid| { - if (sid == .string) break :blk sid.string else break :blk null; - } else break :blk null; - } else null; + if (device.serial_id) |sid| { + try stdout.print("šŸ” Checking recirculation status for {?s}...\n", .{device.device_name}); + try stdout.flush(); - if (serial_id) |sid| { - const device_name = if (device.object.get("device_name")) |n| if (n == .string) n.string else "Unnamed" else "Unnamed"; - try stdout.print("šŸ” Checking recirculation status for {s}...\n", .{device_name}); - try stdout.flush(); + const status = try getRecirculationStatus(allocator, auth.id_token, sid); + defer status.deinit(); - const status = try getRecirculationStatus(allocator, auth.id_token, sid); - defer status.deinit(); + if (status.value.object.get("data")) |status_data| { + if (status_data.object.get("getDeviceShadow")) |shadow| { + try stdout.print("\nCurrent Shadow State:\n", .{}); - if (status.value.object.get("data")) |status_data| { - if (status_data.object.get("getDeviceShadow")) |shadow| { - try stdout.print("\nCurrent Shadow State:\n", .{}); - - if (shadow.object.get("heater_serial_number")) |v| { - if (v == .string) try stdout.print(" heater_serial_number: {s}\n", .{v.string}); - } - if (shadow.object.get("set_recirculation_enabled")) |v| { - if (v == .bool) try stdout.print(" set_recirculation_enabled: {}\n", .{v.bool}); - } - if (shadow.object.get("recirculation_enabled")) |v| { - if (v == .bool) try stdout.print(" recirculation_enabled: {}\n", .{v.bool}); - } - if (shadow.object.get("recirculation_duration")) |v| { - if (v == .integer) try stdout.print(" recirculation_duration: {}\n", .{v.integer}); - } - if (shadow.object.get("set_domestic_temperature")) |v| { - if (v == .integer) try stdout.print(" set_domestic_temperature: {}\n", .{v.integer}); - } - if (shadow.object.get("operation_enabled")) |v| { - if (v == .bool) try stdout.print(" operation_enabled: {}\n", .{v.bool}); - } - - const recirc_enabled = if (shadow.object.get("recirculation_enabled")) |re| if (re == .bool) re.bool else false else false; - - if (recirc_enabled) { - try stdout.print("\nāœ“ Recirculation is already active\n", .{}); - // Recirculation code commented out as requested - } else { - // Recirculation code would go here but not called during testing - try stdout.print("\n(Recirculation start function available but not called during testing)\n", .{}); - } - try stdout.flush(); - } - } - } else { - try stderr.print("āŒ No serial_id found for device\n", .{}); - try stderr.flush(); - return error.NoSerialId; - } - } - } + if (shadow.object.get("heater_serial_number")) |v| { + if (v == .string) try stdout.print(" heater_serial_number: {s}\n", .{v.string}); } + if (shadow.object.get("set_recirculation_enabled")) |v| { + if (v == .bool) try stdout.print(" set_recirculation_enabled: {}\n", .{v.bool}); + } + if (shadow.object.get("recirculation_enabled")) |v| { + if (v == .bool) try stdout.print(" recirculation_enabled: {}\n", .{v.bool}); + } + if (shadow.object.get("recirculation_duration")) |v| { + if (v == .integer) try stdout.print(" recirculation_duration: {}\n", .{v.integer}); + } + if (shadow.object.get("set_domestic_temperature")) |v| { + if (v == .integer) try stdout.print(" set_domestic_temperature: {}\n", .{v.integer}); + } + if (shadow.object.get("operation_enabled")) |v| { + if (v == .bool) try stdout.print(" operation_enabled: {}\n", .{v.bool}); + } + + const recirc_enabled = if (shadow.object.get("recirculation_enabled")) |re| if (re == .bool) re.bool else false else false; + + if (recirc_enabled) { + try stdout.print("\nāœ“ Recirculation is already active\n", .{}); + // Recirculation code commented out as requested + } else { + // Recirculation code would go here but not called during testing + try stdout.print("\n(Recirculation start function available but not called during testing)\n", .{}); + } + try stdout.flush(); } } + } else { + try stderr.print("āŒ No serial_id found for device\n", .{}); + try stderr.flush(); + return error.NoSerialId; } - } else { - try stderr.writeAll("āŒ Error fetching devices. Response: "); - try std.json.Stringify.value(result.value, .{}, stderr); - - try stderr.flush(); } }