"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RangeController = void 0; const catalog_js_1 = require("../catalog.js"); const resources_js_1 = require("../resources.js"); const schema_js_1 = require("../schema.js"); const semantics_js_1 = require("../semantics.js"); const types_js_1 = require("../types.js"); const directives = { SetRangeValue: { name: "SetRangeValue", payload: schema_js_1.s.object({ rangeValue: schema_js_1.s.number() }) }, AdjustRangeValue: { name: "AdjustRangeValue", payload: schema_js_1.s.object({ rangeValueDelta: schema_js_1.s.number(), rangeValueDeltaDefault: schema_js_1.s.boolean() }), note: "rangeValueDeltaDefault is true when the user named no amount; rangeValueDelta is then the precision", }, }; // 0.1 + 0.2 is not 0.3: a value is on the grid when it is within a millionth of a step of it function onGrid(value, origin, step) { const steps = (value - origin) / step; return Math.abs(steps - Math.round(steps)) < 1e-6; } /** * A number in a range, under an instance name: the position of a blind, the speed of a fan. With nonControllable it * is a reading the user can ask for, like an air quality index. */ exports.RangeController = (0, types_js_1.defineInterface)({ namespace: "Alexa.RangeController", version: "3", doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-rangecontroller.html", kind: "controller", tier: 1, instanced: true, properties: { rangeValue: { name: "rangeValue", value: schema_js_1.s.number() }, }, directives, options: schema_js_1.s.object({ /** precision is the step of the range and what "turn up the fan speed" changes it by. */ range: schema_js_1.s.object({ min: schema_js_1.s.number(), max: schema_js_1.s.number(), precision: schema_js_1.s.number({ gt: 0 }) }, { unknownKeys: "reject" }), unit: schema_js_1.s.optional(schema_js_1.s.oneOf(catalog_js_1.UNITS_OF_MEASURE, "a unit of measure like Alexa.Unit.Percent")), /** Names for values: "set the fan speed to maximum". */ presets: schema_js_1.s.optional(schema_js_1.s.array(schema_js_1.s.object({ value: schema_js_1.s.number(), friendlyNames: resources_js_1.labels }, { unknownKeys: "reject" }))), semantics: schema_js_1.s.optional((0, semantics_js_1.semanticsOf)({ directives, stateValue: schema_js_1.s.number(), ranges: true })), }, { unknownKeys: "reject" }), // A capability declared the 1.x way may be without a range; check() says so, discovery lists what there is discovery({ options: { range, unit, presets } }) { const configuration = { ...(range && { supportedRange: { minimumValue: range.min, maximumValue: range.max, precision: range.precision } }), ...(unit && { unitOfMeasure: unit }), ...(presets && { presets: presets.map(({ value, friendlyNames }) => ({ rangeValue: value, presetResources: { friendlyNames } })) }), }; return Object.keys(configuration).length > 0 ? { configuration } : {}; }, validate(capability) { const { range: { min, max, precision }, presets = [], semantics } = capability.options; const inRange = (value) => value >= min && value <= max; if (max <= min) throw new types_js_1.DeclarationError(capability, `range.max ${max} is not above range.min ${min}`); for (const { value } of presets) { // alexa-rangecontroller.html, "Preset object": "minimum range value + (n x precision) where n is an integer" if (!inRange(value) || !onGrid(value, min, precision)) { throw new types_js_1.DeclarationError(capability, `the preset ${value} is not ${min} + n x ${precision}, up to ${max}`); } } for (const { directive } of semantics?.actionMappings ?? []) { const { rangeValue } = directive.payload ?? {}; if (directive.name === "SetRangeValue" && !inRange(rangeValue)) { throw new types_js_1.DeclarationError(capability, `an action is mapped to SetRangeValue ${rangeValue}, the range is ${min} to ${max}`); } } }, // alexa-rangecontroller.html, "SetRangeValue directive error handling": "If your error is safety related, respond // with an Alexa.Safety.ErrorResponse" errorNamespace: "Alexa.Safety", errorTypes: ["OBSTACLE_DETECTED", "SAFETY_BEAM_BREACHED"], });