make pauly shore great again
All checks were successful
Alexa skill build / build (push) Successful in 43s
All checks were successful
Alexa skill build / build (push) Successful in 43s
This commit is contained in:
parent
9cf3035643
commit
56ae5d2039
1 changed files with 27 additions and 23 deletions
50
src/main.zig
50
src/main.zig
|
|
@ -3,7 +3,6 @@ const json = std.json;
|
||||||
const lambda = @import("lambda_runtime");
|
const lambda = @import("lambda_runtime");
|
||||||
const rinnai = @import("rinnai");
|
const rinnai = @import("rinnai");
|
||||||
const homeassistant = @import("homeassistant.zig");
|
const homeassistant = @import("homeassistant.zig");
|
||||||
const alexa = @import("alexa.zig");
|
|
||||||
const timezone = @import("timezone.zig");
|
const timezone = @import("timezone.zig");
|
||||||
const Config = @import("Config.zig");
|
const Config = @import("Config.zig");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|
@ -38,6 +37,7 @@ pub fn main() !u8 {
|
||||||
return runLocal(allocator, config, args);
|
return runLocal(allocator, config, args);
|
||||||
|
|
||||||
const Handler = struct {
|
const Handler = struct {
|
||||||
|
// SAFETY: set 7 lines down...
|
||||||
var c: Config = undefined;
|
var c: Config = undefined;
|
||||||
|
|
||||||
pub fn lambda_handler(alloc: std.mem.Allocator, event_data: []const u8) anyerror![]const u8 {
|
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
|
null, // No timezone needed for toggle
|
||||||
) catch |err| {
|
) catch |err| {
|
||||||
log.err("Home Assistant error: {}", .{err});
|
log.err("Home Assistant error: {}", .{err});
|
||||||
return buildAlexaResponse(allocator, "I had trouble weezin' the juice.", true);
|
return buildAlexaResponse(allocator,
|
||||||
|
\\<voice name="Joey"><prosody rate="x-slow">Whoooaaa</prosody> <prosody volume="loud"><emphasis level="strong">buuuddy.</emphasis> <break time="300ms"/> I had some trouble weezin' the juice, bro.</prosody></voice>
|
||||||
|
, true);
|
||||||
};
|
};
|
||||||
defer allocator.free(result.speech);
|
defer allocator.free(result.speech);
|
||||||
|
|
||||||
return buildAlexaResponse(allocator, "No weezin' the juice!", true);
|
return buildAlexaResponse(allocator,
|
||||||
|
\\<voice name="Joey"><prosody rate="x-slow">Whoooaaa,</prosody> <prosody volume="loud"><emphasis level="strong">no</emphasis> weezin' the <emphasis level="strong">juice!</emphasis></prosody></voice>
|
||||||
|
, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parsed intent parameters for Home Assistant commands
|
/// Parsed intent parameters for Home Assistant commands
|
||||||
|
|
@ -388,7 +392,7 @@ fn resolveLocalTimezone(allocator: std.mem.Allocator) ?i32 {
|
||||||
return timezone.getUtcOffset(allocator, tz_name);
|
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 {
|
fn buildAlexaResponse(allocator: std.mem.Allocator, speech: []const u8, end_session: bool) ![]const u8 {
|
||||||
// Escape speech for JSON
|
// Escape speech for JSON
|
||||||
var escaped_speech: std.ArrayList(u8) = .{};
|
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,
|
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":"<speak>{s}</speak>"}},"shouldEndSession":{s}}}}}
|
||||||
, .{ escaped_speech.items, end_session_str });
|
, .{ 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);
|
try std.testing.expectEqualStrings("1.0", parsed.value.object.get("version").?.string);
|
||||||
const resp = parsed.value.object.get("response").?.object;
|
const resp = parsed.value.object.get("response").?.object;
|
||||||
try std.testing.expect(resp.get("shouldEndSession").?.bool == true);
|
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("SSML", resp.get("outputSpeech").?.object.get("type").?.string);
|
||||||
try std.testing.expectEqualStrings("Hello world", resp.get("outputSpeech").?.object.get("text").?.string);
|
try std.testing.expectEqualStrings("<speak>Hello world</speak>", resp.get("outputSpeech").?.object.get("ssml").?.string);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "buildAlexaResponse with speech and keep session open" {
|
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, .{});
|
const parsed = try json.parseFromSlice(json.Value, allocator, response, .{});
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
const text = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("text").?.string;
|
const ssml = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("ssml").?.string;
|
||||||
try std.testing.expectEqualStrings("Say \"hello\"\nNew line", text);
|
try std.testing.expectEqualStrings("<speak>Say \"hello\"\nNew line</speak>", ssml);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "handler returns error response for invalid JSON" {
|
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, .{});
|
const parsed = try json.parseFromSlice(json.Value, allocator, response, .{});
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
const text = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("text").?.string;
|
const ssml = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("ssml").?.string;
|
||||||
try std.testing.expectEqualStrings("I couldn't understand that request.", text);
|
try std.testing.expectEqualStrings("<speak>I couldn't understand that request.</speak>", ssml);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "handler returns error for missing request field" {
|
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, .{});
|
const parsed = try json.parseFromSlice(json.Value, allocator, response, .{});
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
const text = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("text").?.string;
|
const ssml = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("ssml").?.string;
|
||||||
try std.testing.expectEqualStrings("Invalid request format.", text);
|
try std.testing.expectEqualStrings("<speak>Invalid request format.</speak>", ssml);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "handler handles LaunchRequest" {
|
test "handler handles LaunchRequest" {
|
||||||
|
|
@ -688,8 +692,8 @@ test "handler handles LaunchRequest" {
|
||||||
|
|
||||||
const resp = parsed.value.object.get("response").?.object;
|
const resp = parsed.value.object.get("response").?.object;
|
||||||
try std.testing.expect(resp.get("shouldEndSession").?.bool == false);
|
try std.testing.expect(resp.get("shouldEndSession").?.bool == false);
|
||||||
const text = resp.get("outputSpeech").?.object.get("text").?.string;
|
const ssml = resp.get("outputSpeech").?.object.get("ssml").?.string;
|
||||||
try std.testing.expect(std.mem.indexOf(u8, text, "hot water") != null);
|
try std.testing.expect(std.mem.indexOf(u8, ssml, "hot water") != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "handler handles SessionEndedRequest" {
|
test "handler handles SessionEndedRequest" {
|
||||||
|
|
@ -723,8 +727,8 @@ test "handler handles AMAZON.HelpIntent" {
|
||||||
|
|
||||||
const resp = parsed.value.object.get("response").?.object;
|
const resp = parsed.value.object.get("response").?.object;
|
||||||
try std.testing.expect(resp.get("shouldEndSession").?.bool == false);
|
try std.testing.expect(resp.get("shouldEndSession").?.bool == false);
|
||||||
const text = resp.get("outputSpeech").?.object.get("text").?.string;
|
const ssml = resp.get("outputSpeech").?.object.get("ssml").?.string;
|
||||||
try std.testing.expect(std.mem.indexOf(u8, text, "recirculation") != null);
|
try std.testing.expect(std.mem.indexOf(u8, ssml, "recirculation") != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "handler handles AMAZON.StopIntent" {
|
test "handler handles AMAZON.StopIntent" {
|
||||||
|
|
@ -741,8 +745,8 @@ test "handler handles AMAZON.StopIntent" {
|
||||||
|
|
||||||
const resp = parsed.value.object.get("response").?.object;
|
const resp = parsed.value.object.get("response").?.object;
|
||||||
try std.testing.expect(resp.get("shouldEndSession").?.bool == true);
|
try std.testing.expect(resp.get("shouldEndSession").?.bool == true);
|
||||||
const text = resp.get("outputSpeech").?.object.get("text").?.string;
|
const ssml = resp.get("outputSpeech").?.object.get("ssml").?.string;
|
||||||
try std.testing.expectEqualStrings("Okay, goodbye.", text);
|
try std.testing.expectEqualStrings("<speak>Okay, goodbye.</speak>", ssml);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "handler handles AMAZON.CancelIntent" {
|
test "handler handles AMAZON.CancelIntent" {
|
||||||
|
|
@ -773,8 +777,8 @@ test "handler handles unknown intent" {
|
||||||
const parsed = try json.parseFromSlice(json.Value, allocator, response, .{});
|
const parsed = try json.parseFromSlice(json.Value, allocator, response, .{});
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
const text = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("text").?.string;
|
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.", text);
|
try std.testing.expectEqualStrings("<speak>I don't know how to do that.</speak>", ssml);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "handler handles unknown request type" {
|
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, .{});
|
const parsed = try json.parseFromSlice(json.Value, allocator, response, .{});
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
const text = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("text").?.string;
|
const ssml = parsed.value.object.get("response").?.object.get("outputSpeech").?.object.get("ssml").?.string;
|
||||||
try std.testing.expectEqualStrings("I didn't understand that.", text);
|
try std.testing.expectEqualStrings("<speak>I didn't understand that.</speak>", ssml);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue