diff --git a/src/main.zig b/src/main.zig index 2033d90..ae97447 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3,7 +3,6 @@ const json = std.json; const lambda = @import("lambda_runtime"); const rinnai = @import("rinnai"); const homeassistant = @import("homeassistant.zig"); -const alexa = @import("alexa.zig"); const timezone = @import("timezone.zig"); const Config = @import("Config.zig"); const builtin = @import("builtin"); @@ -38,6 +37,7 @@ pub fn main() !u8 { return runLocal(allocator, config, args); const Handler = struct { + // SAFETY: set 7 lines down... var c: Config = undefined; pub fn lambda_handler(alloc: std.mem.Allocator, event_data: []const u8) anyerror![]const u8 { @@ -259,11 +259,15 @@ fn handleWeezTheJuice(allocator: std.mem.Allocator, config: Config) ![]const u8 null, // No timezone needed for toggle ) catch |err| { log.err("Home Assistant error: {}", .{err}); - return buildAlexaResponse(allocator, "I had trouble weezin' the juice.", true); + return buildAlexaResponse(allocator, + \\Whoooaaa buuuddy. I had some trouble weezin' the juice, bro. + , true); }; defer allocator.free(result.speech); - return buildAlexaResponse(allocator, "No weezin' the juice!", true); + return buildAlexaResponse(allocator, + \\Whoooaaa, no weezin' the juice! + , true); } /// Parsed intent parameters for Home Assistant commands @@ -388,7 +392,7 @@ fn resolveLocalTimezone(allocator: std.mem.Allocator) ?i32 { return timezone.getUtcOffset(allocator, tz_name); } -/// Build an Alexa skill response JSON +/// Build an Alexa skill response JSON with SSML fn buildAlexaResponse(allocator: std.mem.Allocator, speech: []const u8, end_session: bool) ![]const u8 { // Escape speech for JSON var escaped_speech: std.ArrayList(u8) = .{}; @@ -415,7 +419,7 @@ fn buildAlexaResponse(allocator: std.mem.Allocator, speech: []const u8, end_sess } return try std.fmt.allocPrint(allocator, - \\{{"version":"1.0","response":{{"outputSpeech":{{"type":"PlainText","text":"{s}"}},"shouldEndSession":{s}}}}} + \\{{"version":"1.0","response":{{"outputSpeech":{{"type":"SSML","ssml":"{s}"}},"shouldEndSession":{s}}}}} , .{ escaped_speech.items, end_session_str }); } @@ -606,8 +610,8 @@ test "buildAlexaResponse with speech and end session" { try std.testing.expectEqualStrings("1.0", parsed.value.object.get("version").?.string); const resp = parsed.value.object.get("response").?.object; try std.testing.expect(resp.get("shouldEndSession").?.bool == true); - try std.testing.expectEqualStrings("PlainText", resp.get("outputSpeech").?.object.get("type").?.string); - try std.testing.expectEqualStrings("Hello world", resp.get("outputSpeech").?.object.get("text").?.string); + try std.testing.expectEqualStrings("SSML", resp.get("outputSpeech").?.object.get("type").?.string); + try std.testing.expectEqualStrings("Hello world", resp.get("outputSpeech").?.object.get("ssml").?.string); } test "buildAlexaResponse with speech and keep session open" { @@ -644,8 +648,8 @@ test "buildAlexaResponse escapes special characters" { const parsed = try json.parseFromSlice(json.Value, allocator, response, .{}); defer parsed.deinit(); - const text = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("text").?.string; - try std.testing.expectEqualStrings("Say \"hello\"\nNew line", text); + const ssml = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("ssml").?.string; + try std.testing.expectEqualStrings("Say \"hello\"\nNew line", ssml); } test "handler returns error response for invalid JSON" { @@ -657,8 +661,8 @@ test "handler returns error response for invalid JSON" { const parsed = try json.parseFromSlice(json.Value, allocator, response, .{}); defer parsed.deinit(); - const text = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("text").?.string; - try std.testing.expectEqualStrings("I couldn't understand that request.", text); + const ssml = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("ssml").?.string; + try std.testing.expectEqualStrings("I couldn't understand that request.", ssml); } test "handler returns error for missing request field" { @@ -670,8 +674,8 @@ test "handler returns error for missing request field" { const parsed = try json.parseFromSlice(json.Value, allocator, response, .{}); defer parsed.deinit(); - const text = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("text").?.string; - try std.testing.expectEqualStrings("Invalid request format.", text); + const ssml = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("ssml").?.string; + try std.testing.expectEqualStrings("Invalid request format.", ssml); } test "handler handles LaunchRequest" { @@ -688,8 +692,8 @@ test "handler handles LaunchRequest" { const resp = parsed.value.object.get("response").?.object; try std.testing.expect(resp.get("shouldEndSession").?.bool == false); - const text = resp.get("outputSpeech").?.object.get("text").?.string; - try std.testing.expect(std.mem.indexOf(u8, text, "hot water") != null); + const ssml = resp.get("outputSpeech").?.object.get("ssml").?.string; + try std.testing.expect(std.mem.indexOf(u8, ssml, "hot water") != null); } test "handler handles SessionEndedRequest" { @@ -723,8 +727,8 @@ test "handler handles AMAZON.HelpIntent" { const resp = parsed.value.object.get("response").?.object; try std.testing.expect(resp.get("shouldEndSession").?.bool == false); - const text = resp.get("outputSpeech").?.object.get("text").?.string; - try std.testing.expect(std.mem.indexOf(u8, text, "recirculation") != null); + const ssml = resp.get("outputSpeech").?.object.get("ssml").?.string; + try std.testing.expect(std.mem.indexOf(u8, ssml, "recirculation") != null); } test "handler handles AMAZON.StopIntent" { @@ -741,8 +745,8 @@ test "handler handles AMAZON.StopIntent" { const resp = parsed.value.object.get("response").?.object; try std.testing.expect(resp.get("shouldEndSession").?.bool == true); - const text = resp.get("outputSpeech").?.object.get("text").?.string; - try std.testing.expectEqualStrings("Okay, goodbye.", text); + const ssml = resp.get("outputSpeech").?.object.get("ssml").?.string; + try std.testing.expectEqualStrings("Okay, goodbye.", ssml); } test "handler handles AMAZON.CancelIntent" { @@ -773,8 +777,8 @@ test "handler handles unknown intent" { const parsed = try json.parseFromSlice(json.Value, allocator, response, .{}); defer parsed.deinit(); - const text = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("text").?.string; - try std.testing.expectEqualStrings("I don't know how to do that.", text); + const ssml = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("ssml").?.string; + try std.testing.expectEqualStrings("I don't know how to do that.", ssml); } test "handler handles unknown request type" { @@ -789,8 +793,8 @@ test "handler handles unknown request type" { const parsed = try json.parseFromSlice(json.Value, allocator, response, .{}); defer parsed.deinit(); - const text = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("text").?.string; - try std.testing.expectEqualStrings("I didn't understand that.", text); + const ssml = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("ssml").?.string; + try std.testing.expectEqualStrings("I didn't understand that.", ssml); } // =============================================================================