registry: ThermostatController 3.2 and ThermostatController.Schedule
The thermostat is described from its page: one, two or three setpoints with holdUntil, AdjustTargetTemperature, SetThermostatMode with the six modes (EM_HEAT is new), ResumeSchedule, adaptiveRecoveryStatus, and its seven error types under Alexa.ThermostatController. A declaration gives supportedModes, supportsScheduling and the properties the device has; without them discovery is what 1.5.2 sent: HEAT, COOL, AUTO, OFF and the three setpoints with thermostatMode. supportedModes without HEAT or COOL is refused where it is declared. ThermostatController.Schedule replaces its stub. Wire change for a 1.x declaration of it: version 3.2 (was 1) and scheduleEnabled listed (was nothing). SetAdaptiveRecovery is taken only with supportsAdaptiveRecovery. 24 examples of three pages are saved as fixtures; the page folds the SetWeeklySchedule example away, its payload is tested from the tables. 207 tests pass, 191 before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
af1489696d
commit
f555a5df70
67 changed files with 2445 additions and 58 deletions
77
dist/esm/registry/interfaces/ThermostatControllerSchedule.js
vendored
Normal file
77
dist/esm/registry/interfaces/ThermostatControllerSchedule.js
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import { s } from "../schema.js";
|
||||
import { defineInterface } from "../types.js";
|
||||
const FAN_MODES = ["ON", "AUTO", "CIRCULATE"];
|
||||
// alexa-thermostatcontroller-schedule.html, "ScheduleEntry object": an entry ends where the next one starts
|
||||
const entry = s.object({
|
||||
startTimeInMinutes: s.number({ min: 0, max: 1439, integer: true }),
|
||||
setpoints: s.object({ upperSetpoint: s.optional(s.temperature()), lowerSetpoint: s.optional(s.temperature()) }),
|
||||
/** Without a mode the fan runs as the manufacturer set it. */
|
||||
fanSetting: s.optional(s.object({ mode: s.optional(s.enum(...FAN_MODES)) })),
|
||||
activityType: s.optional(s.object({ type: s.enum("AWAY", "HOME", "SLEEP") })),
|
||||
});
|
||||
const day = s.array(entry);
|
||||
// Same page, "WeeklySchedule object"
|
||||
const weeklySchedule = s.object({
|
||||
temperatureScale: s.enum("CELSIUS", "FAHRENHEIT", "KELVIN"),
|
||||
Monday: day,
|
||||
Tuesday: day,
|
||||
Wednesday: day,
|
||||
Thursday: day,
|
||||
Friday: day,
|
||||
Saturday: day,
|
||||
Sunday: day,
|
||||
});
|
||||
/**
|
||||
* Version 3.2. The schedule a user writes in the Alexa app: the interface has no utterances. Discovery lists
|
||||
* adaptiveRecoveryEnabled for a thermostat declared with supportsAdaptiveRecovery, and Alexa sends
|
||||
* SetAdaptiveRecovery to such a thermostat only.
|
||||
*/
|
||||
export const ThermostatControllerSchedule = defineInterface({
|
||||
namespace: "Alexa.ThermostatController.Schedule",
|
||||
version: "3.2",
|
||||
doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-thermostatcontroller-schedule.html",
|
||||
kind: "controller",
|
||||
tier: 2,
|
||||
instanced: false,
|
||||
properties: {
|
||||
adaptiveRecoveryEnabled: { name: "adaptiveRecoveryEnabled", value: s.boolean() },
|
||||
scheduleEnabled: { name: "scheduleEnabled", value: s.boolean() },
|
||||
},
|
||||
directives: {
|
||||
SetWeeklySchedule: {
|
||||
name: "SetWeeklySchedule",
|
||||
payload: s.object({ weeklySchedule: s.optional(weeklySchedule) }),
|
||||
note: "a schedule the thermostat cannot keep leaves the one it has and is answered with an error",
|
||||
},
|
||||
SetScheduleState: {
|
||||
name: "SetScheduleState",
|
||||
payload: s.object({ scheduleEnabled: s.boolean() }),
|
||||
note: "a disabled schedule is kept: the user can enable it again",
|
||||
},
|
||||
SetAdaptiveRecovery: {
|
||||
name: "SetAdaptiveRecovery",
|
||||
payload: s.object({ adaptiveRecoveryEnabled: s.boolean() }),
|
||||
when: ({ options }) => options.supportsAdaptiveRecovery === true,
|
||||
},
|
||||
},
|
||||
options: s.object({
|
||||
supportedFanModes: s.array(s.enum(...FAN_MODES)),
|
||||
/** The thermostat heats or cools ahead of an entry, to be at its temperature when it starts. */
|
||||
supportsAdaptiveRecovery: s.boolean(),
|
||||
maxEntryPerDay: s.optional(s.number({ min: 1, integer: true })),
|
||||
}, { unknownKeys: "reject" }),
|
||||
// A capability declared the 1.x way has none of the options; check() says so, discovery lists what there is
|
||||
discovery({ options: { supportedFanModes, supportsAdaptiveRecovery, maxEntryPerDay } }) {
|
||||
const configuration = {
|
||||
...(supportedFanModes && { supportedFanModes }),
|
||||
...(supportsAdaptiveRecovery !== undefined && { supportsAdaptiveRecovery }),
|
||||
...(maxEntryPerDay !== undefined && { maxEntryPerDay }),
|
||||
};
|
||||
return {
|
||||
supported: supportsAdaptiveRecovery ? ["adaptiveRecoveryEnabled", "scheduleEnabled"] : ["scheduleEnabled"],
|
||||
...(Object.keys(configuration).length > 0 && { configuration }),
|
||||
};
|
||||
},
|
||||
errorNamespace: "Alexa.ThermostatController.Schedule",
|
||||
errorTypes: ["INSUFFICIENT_SPACE"],
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue