Advertisement
dragonbane

alexaSmartFritz Lambda Function

Feb 20th, 2018
311
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 27.31 KB | None | 0 0
  1. 'use strict';
  2.  
  3. var WebSocket = require('ws');
  4. var request = require('request');
  5. var uuid = require('uuid-v4');
  6.  
  7. /**
  8. * This sample demonstrates a smart home skill using the publicly available API on Amazon's Alexa platform.
  9. * For more information about developing smart home skills, see
  10. * https://developer.amazon.com/alexa/smart-home
  11. *
  12. * For details on the smart home API, please visit
  13. * https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/smart-home-skill-api-reference
  14. */
  15.  
  16. const USER_DEVICES = [
  17. {
  18. // This id needs to be unique across all devices discovered for a given manufacturer
  19. endpointId: '119600650576',
  20. // The name given by the user in your application. Examples include 'Bedroom light' etc
  21. friendlyName: 'K�chen Thermostat',
  22. // Should describe the device type and the company/cloud provider.
  23. // This value will be shown in the Alexa app
  24. description: 'Thermostat f�r Fritz!Box',
  25. // Company name that produces and sells the smart home device
  26. manufacturerName: 'Eurotronic',
  27. displayCategories: ['THERMOSTAT', 'TEMPERATURE_SENSOR'],
  28. // not used at this time
  29. cookie: {
  30. extraDetail1: 'a',
  31. extraDetail2: 'b',
  32. extraDetail3: 'c',
  33. extraDetail4: 'd',
  34. },
  35. capabilities: [
  36. {
  37. "type": "AlexaInterface",
  38. "interface": "Alexa.EndpointHealth",
  39. "version": "3",
  40. "properties": {
  41. "supported": [
  42. {
  43. "name": "connectivity"
  44. }
  45. ],
  46. "proactivelyReported": true,
  47. "retrievable": true
  48. }
  49. },
  50. {
  51. "type": "AlexaInterface",
  52. "interface": "Alexa.PowerController",
  53. "version": "3",
  54. "properties": {
  55. "supported": [
  56. {
  57. "name": "powerState"
  58. }
  59. ],
  60. "proactivelyReported": true,
  61. "retrievable": true
  62. }
  63. },
  64. {
  65. "type": "AlexaInterface",
  66. "interface": "Alexa.ThermostatController",
  67. "version": "3",
  68. "properties": {
  69. "supported": [
  70. {
  71. "name": "targetSetpoint"
  72. }
  73. ],
  74. "proactivelyReported": true,
  75. "retrievable": true
  76. }
  77. },
  78. {
  79. "type": "AlexaInterface",
  80. "interface": "Alexa.TemperatureSensor",
  81. "version": "3",
  82. "properties": {
  83. "supported": [
  84. {
  85. "name": "temperature"
  86. }
  87. ],
  88. "proactivelyReported": true,
  89. "retrievable": true
  90. }
  91. }]
  92. }
  93. ];
  94.  
  95. /**
  96. * Utility functions
  97. */
  98.  
  99. function log(title, msg) {
  100. console.log(`[${title}] ${msg}`);
  101. }
  102.  
  103. /**
  104. * Generate a unique message ID
  105. *
  106. * TODO: UUID v4 is recommended as a message ID in production.
  107. */
  108. function generateMessageID() {
  109.  
  110. return uuid();
  111. }
  112.  
  113. function generateTimeStamp() {
  114.  
  115. var time = new Date();
  116.  
  117. return time.toISOString();
  118. }
  119.  
  120. /**
  121. * Generate a response message
  122. *
  123. * @param {Object} request - request.directive object
  124. * @param {string} eventType - eventType e.g. Response or StateReport
  125. * @param {Object} contextProperties - static array of values to report
  126. * @param {Object} payload - Any special payload required for the response
  127. * @returns {Object} Response object
  128. */
  129. function generateResponse(request, eventType, contextProperties, payload) {
  130. return {
  131. context: {
  132. properties: contextProperties
  133. },
  134. event: {
  135. header: {
  136. messageId: generateMessageID(),
  137. namespace: 'Alexa',
  138. name: eventType,
  139. correlationToken: request.header.correlationToken,
  140. payloadVersion: '3'
  141. },
  142. endpoint: {
  143. scope: {
  144. type: 'BearerToken',
  145. token: request.endpoint.scope.token
  146. },
  147. endpointId: request.endpoint.endpointId
  148. },
  149. payload: payload
  150. }
  151. };
  152. }
  153.  
  154. /**
  155. * Function to access device cloud.
  156. */
  157.  
  158. function getDevicesFromPartnerCloud() {
  159.  
  160. return USER_DEVICES;
  161. }
  162.  
  163. function transmitCommandToPi(cmdJson) {
  164.  
  165. var socket;
  166.  
  167. console.log("Send Cmd to FritzBox: " + cmdJson);
  168.  
  169. return new Promise((resolve, reject) => {
  170.  
  171. socket = new WebSocket('http://' + 'wkyg3jni0whrfyk6.myfritz.net' + ':9090', function (error) {
  172. console.log(new Error(error).toString());
  173.  
  174. reject("SocketError");
  175. return;
  176. });
  177.  
  178. socket.on('error', function (e) {
  179. console.log('Error in WebSocket communication');
  180. socket.close();
  181.  
  182. reject("SocketError");
  183. return;
  184. });
  185.  
  186. socket.on('message', function (msg) {
  187.  
  188. console.log("incoming message: ", msg);
  189.  
  190. if (msg == "[REQUEST]") {
  191.  
  192. socket.send("[FRITZCMD]=" + cmdJson);
  193. }
  194. else if (msg.startsWith("[END]")) {
  195. let status = msg.substr(msg.indexOf('=') + 1);
  196.  
  197. console.log("Cmd END received with status: " + status + ". Shut down socket!");
  198.  
  199. socket.close();
  200.  
  201. resolve(status);
  202. }
  203. });
  204. });
  205. }
  206.  
  207. //request, name, value, payload
  208.  
  209. function turnThermostatOn(request, applianceId) {
  210. log('DEBUG', `turnOn (applianceId: ${applianceId})`);
  211.  
  212. var fritzCmd = { cmd: "turnOn", id: applianceId };
  213.  
  214. return new Promise((resolve, reject) => {
  215.  
  216. transmitCommandToPi(JSON.stringify(fritzCmd)).then((successMsg, error) => {
  217.  
  218. if (error) {
  219. const errorMessage = `Leider ist bei der �bermittlung des Fritz!Box Befehls ein Problem aufgetreten!`;
  220. log('ERROR', errorMessage);
  221.  
  222. reject(generateResponse(request, 'UnsupportedOperationError', {}, {}));
  223. }
  224. else {
  225.  
  226. var resultJSON = JSON.parse(successMsg);
  227.  
  228. console.log(successMsg);
  229.  
  230. if (resultJSON.result == "error") {
  231. reject(generateResponse(request, 'UnsupportedOperationError', {}, {}));
  232.  
  233. return;
  234. }
  235.  
  236. var contextProperties = [{
  237. namespace: request.header.namespace,
  238. name: "powerState",
  239. value: "ON",
  240. timeOfSample: generateTimeStamp(),
  241. uncertaintyInMilliseconds: 900000 //15 minutes uncertainy due fritz box polling every 15 minutes
  242. }];
  243.  
  244. resolve(generateResponse(request, "Response", contextProperties, {}));
  245. }
  246. });
  247. });
  248. }
  249.  
  250. function turnThermostatOff(request, applianceId) {
  251. log('DEBUG', `turnOff (applianceId: ${applianceId})`);
  252.  
  253. var fritzCmd = { cmd: "turnOff", id: applianceId };
  254.  
  255. return new Promise((resolve, reject) => {
  256.  
  257. transmitCommandToPi(JSON.stringify(fritzCmd)).then((successMsg, error) => {
  258.  
  259. if (error) {
  260. const errorMessage = `Leider ist bei der �bermittlung des Fritz!Box Befehls ein Problem aufgetreten!`;
  261. log('ERROR', errorMessage);
  262.  
  263. reject(generateResponse(request, 'UnsupportedOperationError', {}, {}));
  264. }
  265. else {
  266.  
  267. var resultJSON = JSON.parse(successMsg);
  268.  
  269. console.log(successMsg);
  270.  
  271. if (resultJSON.result == "error") {
  272. reject(generateResponse(request, 'UnsupportedOperationError', {}, {}));
  273.  
  274. return;
  275. }
  276.  
  277. var contextProperties = [{
  278. namespace: request.header.namespace,
  279. name: "powerState",
  280. value: "OFF",
  281. timeOfSample: generateTimeStamp(),
  282. uncertaintyInMilliseconds: 900000 //15 minutes uncertainy due fritz box polling every 15 minutes
  283. }];
  284.  
  285. resolve(generateResponse(request, "Response", contextProperties, {}));
  286. }
  287. });
  288. });
  289. }
  290.  
  291. function queryThermostatTemp(request, applianceId) {
  292.  
  293. log('DEBUG', `Query Temp (applianceId: ${applianceId})`);
  294.  
  295. var fritzCmd = { cmd: "getStats", id: applianceId };
  296.  
  297. return new Promise((resolve, reject) => {
  298.  
  299. transmitCommandToPi(JSON.stringify(fritzCmd)).then((successMsg, error) => {
  300.  
  301. if (error) {
  302. const errorMessage = `Leider ist bei der �bermittlung des Fritz!Box Befehls ein Problem aufgetreten!`;
  303. log('ERROR', errorMessage);
  304.  
  305. reject(generateResponse(request, 'UnsupportedOperationError', {}, {}));
  306. }
  307. else {
  308.  
  309. var resultJSON = JSON.parse(successMsg);
  310.  
  311. console.log(successMsg);
  312.  
  313. var contextProperties;
  314.  
  315. if (resultJSON.result == "error")
  316.  
  317. contextProperties = [
  318. {
  319. namespace: "Alexa.EndpointHealth",
  320. name: "connectivity",
  321. value: "UNREACHABLE", //OK or UNREACHABLE
  322. timeOfSample: generateTimeStamp(),
  323. uncertaintyInMilliseconds: 900000 //15 minutes uncertainy due fritz box polling every 15 minutes
  324. },
  325. {
  326. namespace: "Alexa.PowerController",
  327. name: "powerState",
  328. value: "OFF", //ON or OFF
  329. timeOfSample: generateTimeStamp(),
  330. uncertaintyInMilliseconds: 900000 //15 minutes uncertainy due fritz box polling every 15 minutes
  331. }
  332. ];
  333. else {
  334.  
  335. contextProperties = [
  336. {
  337. namespace: "Alexa.EndpointHealth",
  338. name: "connectivity",
  339. value: "OK", //OK or UNREACHABLE
  340. timeOfSample: generateTimeStamp(),
  341. uncertaintyInMilliseconds: 900000 //15 minutes uncertainy due fritz box polling every 15 minutes
  342. },
  343. {
  344. namespace: "Alexa.PowerController",
  345. name: "powerState",
  346. value: "ON", //ON or OFF
  347. timeOfSample: generateTimeStamp(),
  348. uncertaintyInMilliseconds: 900000 //15 minutes uncertainy due fritz box polling every 15 minutes
  349. },
  350. {
  351. namespace: "Alexa.TemperatureSensor",
  352. name: "temperature",
  353. value: { value: resultJSON.temperature, scale: "CELSIUS" },
  354. timeOfSample: generateTimeStamp(),
  355. uncertaintyInMilliseconds: 900000 //15 minutes uncertainy due fritz box polling every 15 minutes
  356. },
  357. {
  358. namespace: "Alexa.ThermostatController",
  359. name: "targetSetpoint",
  360. value: { value: resultJSON.targetSetpoint, scale: "CELSIUS" },
  361. timeOfSample: generateTimeStamp(),
  362. uncertaintyInMilliseconds: 900000 //15 minutes uncertainy due fritz box polling every 15 minutes
  363. }
  364. ];
  365. }
  366.  
  367. resolve(generateResponse(request, "StateReport", contextProperties, {}));
  368. }
  369. });
  370. });
  371. }
  372.  
  373. function adjustTargetTemp(request, applianceId) {
  374. log('DEBUG', `adjustTargetTemp (applianceId: ${applianceId}, adjustTemp: ${request.payload.targetSetpointDelta.value})`);
  375.  
  376. var adjustTemp = request.payload.targetSetpointDelta.value;
  377.  
  378. var fritzCmd = { cmd: "adjustTemp", id: applianceId, delta: adjustTemp};
  379.  
  380. return new Promise((resolve, reject) => {
  381.  
  382. transmitCommandToPi(JSON.stringify(fritzCmd)).then((successMsg, error) => {
  383.  
  384. if (error) {
  385. const errorMessage = `Leider ist bei der �bermittlung des Fritz!Box Befehls ein Problem aufgetreten!`;
  386. log('ERROR', errorMessage);
  387.  
  388. reject(generateResponse(request, 'UnsupportedOperationError', {}, {}));
  389. }
  390. else {
  391.  
  392. var resultJSON = JSON.parse(successMsg);
  393.  
  394. console.log(successMsg);
  395.  
  396. if (resultJSON.result == "error") {
  397. reject(generateResponse(request, 'UnsupportedOperationError', {}, {}));
  398.  
  399. return;
  400. }
  401.  
  402. var contextProperties = [{
  403. namespace: request.header.namespace,
  404. name: "targetSetpoint",
  405. value: {
  406. value: resultJSON.target, scale: "CELSIUS"
  407. },
  408. timeOfSample: generateTimeStamp(),
  409. uncertaintyInMilliseconds: 900000 //15 minutes uncertainy due fritz box polling every 15 minutes
  410. }];
  411.  
  412. resolve(generateResponse(request, "Response", contextProperties, {}));
  413. }
  414. });
  415. });
  416. }
  417.  
  418. function setTargetTemp(request, applianceId) {
  419. log('DEBUG', `setTargetTemp (applianceId: ${applianceId}, targetTemp: ${request.payload.targetSetpoint.value})`);
  420.  
  421. var targetTemp = request.payload.targetSetpoint.value;
  422.  
  423. var fritzCmd = { cmd: "setTemp", id: applianceId, target: targetTemp };
  424.  
  425. return new Promise((resolve, reject) => {
  426.  
  427. transmitCommandToPi(JSON.stringify(fritzCmd)).then((successMsg, error) => {
  428.  
  429. if (error) {
  430. const errorMessage = `Leider ist bei der �bermittlung des Fritz!Box Befehls ein Problem aufgetreten!`;
  431. log('ERROR', errorMessage);
  432.  
  433. reject(generateResponse(request, 'UnsupportedOperationError', {}, {}));
  434. }
  435. else {
  436.  
  437. var resultJSON = JSON.parse(successMsg);
  438.  
  439. console.log(successMsg);
  440.  
  441. if (resultJSON.result == "error") {
  442. reject(generateResponse(request, 'UnsupportedOperationError', {}, {}));
  443.  
  444. return;
  445. }
  446.  
  447. var contextProperties = [{
  448. namespace: request.header.namespace,
  449. name: "targetSetpoint",
  450. value: {
  451. value: resultJSON.target, scale: "CELSIUS"
  452. },
  453. timeOfSample: generateTimeStamp(),
  454. uncertaintyInMilliseconds: 900000 //15 minutes uncertainy due fritz box polling every 15 minutes
  455. }];
  456.  
  457. resolve(generateResponse(request, "Response", contextProperties, {}));
  458. }
  459. });
  460. });
  461. }
  462.  
  463.  
  464. /**
  465. * Main logic
  466. */
  467.  
  468. /**
  469. * This function is invoked when we receive a "Discovery" message from Alexa Smart Home Skill.
  470. * We are expected to respond back with a list of appliances that we have discovered for a given customer.
  471. *
  472. * @param {Object} request - The full request object from the Alexa smart home service. This represents a DiscoverAppliancesRequest.
  473. * https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/smart-home-skill-api-reference#discoverappliancesrequest
  474. *
  475. * @param {function} callback - The callback object on which to succeed or fail the response.
  476. * https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html#nodejs-prog-model-handler-callback
  477. * If successful, return <DiscoverAppliancesResponse>.
  478. * https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/smart-home-skill-api-reference#discoverappliancesresponse
  479. */
  480.  
  481. function handleAuth(request, callback) {
  482.  
  483. console.log("User Tokens for Message Gateway: " + request);
  484.  
  485. const response = {
  486. event: {
  487. header: {
  488. messageId: generateMessageID(),
  489. name: 'AcceptGrant.Response',
  490. namespace: 'Alexa.Authorization',
  491. payloadVersion: '3',
  492. },
  493. payload: {
  494. },
  495. }
  496. };
  497.  
  498. callback(null, response);
  499. }
  500.  
  501. function handleDiscovery(request, callback) {
  502. log('DEBUG', `Discovery Request: ${JSON.stringify(request)}`);
  503.  
  504. /**
  505. * Get the OAuth token from the request.
  506. */
  507. const userAccessToken = request.payload.scope.token.trim();
  508.  
  509. if (!userAccessToken) {
  510. const errorMessage = `Discovery Request [${request.header.messageId}] failed. Invalid access token: ${userAccessToken}`;
  511. log('ERROR', errorMessage);
  512. callback(new Error(errorMessage));
  513. }
  514.  
  515. /**
  516. * Assume access token is valid at this point.
  517. * Retrieve list of devices from cloud based on token.
  518. *
  519. * For more information on a discovery response see
  520. * https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/smart-home-skill-api-reference#discoverappliancesresponse
  521. */
  522. const response = {
  523. event: {
  524. header: {
  525. messageId: generateMessageID(),
  526. name: 'Discover.Response',
  527. namespace: 'Alexa.Discovery',
  528. payloadVersion: '3',
  529. },
  530. payload: {
  531. endpoints: getDevicesFromPartnerCloud(userAccessToken),
  532. },
  533. }
  534. };
  535.  
  536. /**
  537. * Log the response. These messages will be stored in CloudWatch.
  538. */
  539. log('DEBUG', `Discovery Response: ${JSON.stringify(response)}`);
  540.  
  541. /**
  542. * Return result with successful message.
  543. */
  544. callback(null, response);
  545. }
  546.  
  547. /**
  548. * A function to handle power control events.
  549. * This is called when Alexa requests an action such as turning off an appliance.
  550. *
  551. * @param {Object} request - The full request object from the Alexa smart home service.
  552. * @param {function} callback - The callback object on which to succeed or fail the response.
  553. */
  554. function handlePowerControl(request, callback) {
  555. log('DEBUG', `Control Request: ${JSON.stringify(request)}`);
  556.  
  557. /**
  558. * Get the access token.
  559. */
  560. const userAccessToken = request.endpoint.scope.token.trim();
  561.  
  562. if (!userAccessToken) {
  563. log('ERROR', `Power Request [${request.header.messageId}] failed. Invalid access token: ${userAccessToken}`);
  564. callback(null, generateResponse(request, 'InvalidAccessTokenError', {}, {}));
  565. return;
  566. }
  567.  
  568. /**
  569. * Grab the applianceId from the request.
  570. */
  571. const applianceId = request.endpoint.endpointId;
  572.  
  573. /**
  574. * If the applianceId is missing, return UnexpectedInformationReceivedError
  575. * https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/smart-home-skill-api-reference#unexpectedinformationreceivederror
  576. */
  577. if (!applianceId) {
  578. log('ERROR', 'No applianceId provided in request');
  579. const payload = { faultingParameter: `applianceId: ${applianceId}` };
  580. callback(null, generateResponse(request, 'UnexpectedInformationReceivedError', {}, payload));
  581. return;
  582. }
  583.  
  584. /**
  585. * At this point the applianceId and accessToken are present in the request.
  586. *
  587. */
  588.  
  589. let response;
  590.  
  591. switch (request.header.name) {
  592. case 'TurnOn': {
  593. turnThermostatOn(request, applianceId).then((successMsg, error) => {
  594.  
  595. if (error)
  596. response = error;
  597. else
  598. response = successMsg;
  599.  
  600. log('DEBUG', `Control Confirmation: ${JSON.stringify(response)}`);
  601.  
  602. callback(null, response);
  603. });
  604.  
  605. break;
  606. }
  607. case 'TurnOff': {
  608. turnThermostatOff(request, applianceId).then((successMsg, error) => {
  609.  
  610. if (error)
  611. response = error;
  612. else
  613. response = successMsg;
  614.  
  615. log('DEBUG', `Control Confirmation: ${JSON.stringify(response)}`);
  616.  
  617. callback(null, response);
  618. });
  619.  
  620. break;
  621. }
  622. default: {
  623. log('ERROR', `No supported directive event name: ${request.header.name}`);
  624. callback(null, generateResponse(request, 'UnsupportedOperationError', {}, {}));
  625. return;
  626. }
  627. }
  628. }
  629.  
  630. function handleQuery(request, callback) {
  631.  
  632. log('DEBUG', `Query Request: ${JSON.stringify(request)}`);
  633.  
  634. /**
  635. * Get the access token.
  636. */
  637. const userAccessToken = request.endpoint.scope.token.trim();
  638.  
  639. if (!userAccessToken) {
  640. log('ERROR', `Query Request [${request.header.messageId}] failed. Invalid access token: ${userAccessToken}`);
  641. callback(null, generateResponse(request, 'InvalidAccessTokenError', {}, {}));
  642. return;
  643. }
  644.  
  645. /**
  646. * Grab the applianceId from the request.
  647. */
  648. const applianceId = request.endpoint.endpointId;
  649.  
  650. /**
  651. * If the applianceId is missing, return UnexpectedInformationReceivedError
  652. * https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/smart-home-skill-api-reference#unexpectedinformationreceivederror
  653. */
  654. if (!applianceId) {
  655. log('ERROR', 'No applianceId provided in request');
  656. const payload = { faultingParameter: `applianceId: ${applianceId}` };
  657. callback(null, generateResponse(request, 'UnexpectedInformationReceivedError', {}, payload));
  658. return;
  659. }
  660.  
  661. let response;
  662.  
  663. switch (request.header.name) {
  664. case 'ReportState': {
  665. queryThermostatTemp(request, applianceId).then((successMsg, error) => {
  666.  
  667. if (error)
  668. response = error;
  669. else
  670. response = successMsg;
  671.  
  672. log('DEBUG', `Query Confirmation: ${JSON.stringify(response)}`);
  673.  
  674. callback(null, response);
  675. });
  676.  
  677. break;
  678. }
  679. default: {
  680. log('ERROR', `No supported directive event name: ${request.header.name}`);
  681. callback(null, generateResponse(request, 'UnsupportedOperationError', {}, {}));
  682. return;
  683. }
  684. }
  685. }
  686.  
  687. function handleThermostatControl(request, callback) {
  688.  
  689. log('DEBUG', `SetThermostat Request: ${JSON.stringify(request)}`);
  690.  
  691. /**
  692. * Get the access token.
  693. */
  694. const userAccessToken = request.endpoint.scope.token.trim();
  695.  
  696. if (!userAccessToken) {
  697. log('ERROR', `SetThermostat Request [${request.header.messageId}] failed. Invalid access token: ${userAccessToken}`);
  698. callback(null, generateResponse(request, 'InvalidAccessTokenError', {}, {}));
  699. return;
  700. }
  701.  
  702. /**
  703. * Grab the applianceId from the request.
  704. */
  705. const applianceId = request.endpoint.endpointId;
  706.  
  707. /**
  708. * If the applianceId is missing, return UnexpectedInformationReceivedError
  709. * https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/smart-home-skill-api-reference#unexpectedinformationreceivederror
  710. */
  711. if (!applianceId) {
  712. log('ERROR', 'No applianceId provided in request');
  713. const payload = { faultingParameter: `applianceId: ${applianceId}` };
  714. callback(null, generateResponse(request, 'UnexpectedInformationReceivedError', {}, payload));
  715. return;
  716. }
  717.  
  718. /**
  719. * At this point the applianceId and accessToken are present in the request.
  720. *
  721. */
  722.  
  723. let response;
  724.  
  725. switch (request.header.name) {
  726. case 'SetTargetTemperature': {
  727.  
  728. setTargetTemp(request, applianceId).then((successMsg, error) => {
  729.  
  730. if (error)
  731. response = error;
  732. else
  733. response = successMsg;
  734.  
  735. log('DEBUG', `SetThermostat Confirmation: ${JSON.stringify(response)}`);
  736.  
  737. callback(null, response);
  738. });
  739.  
  740. break;
  741. }
  742. case 'AdjustTargetTemperature': {
  743. adjustTargetTemp(request, applianceId).then((successMsg, error) => {
  744.  
  745. if (error)
  746. response = error;
  747. else
  748. response = successMsg;
  749.  
  750. log('DEBUG', `SetThermostat Confirmation: ${JSON.stringify(response)}`);
  751.  
  752. callback(null, response);
  753. });
  754.  
  755. break;
  756. }
  757. default: {
  758. log('ERROR', `No supported directive event name: ${request.header.name}`);
  759. callback(null, generateResponse(request, 'UnsupportedOperationError', {}, {}));
  760. return;
  761. }
  762. }
  763. }
  764.  
  765.  
  766. /**
  767. * Main entry point.
  768. * Incoming events from Alexa service through Smart Home API are all handled by this function.
  769. *
  770. * It is recommended to validate the request and response with Alexa Smart Home Skill API Validation package.
  771. * https://github.com/alexa/alexa-smarthome-validation
  772. */
  773. exports.handler = (request, context, callback) => {
  774. console.log(request);
  775.  
  776. switch (request.directive.header.namespace) {
  777. case 'Alexa.Authorization':
  778. handleAuth(request.directive, callback);
  779. break;
  780. /**
  781. * The namespace of 'Alexa.Discovery' indicates a request is being made to the Lambda for
  782. * discovering all appliances associated with the customer's appliance cloud account.
  783. *
  784. * For more information on device discovery, please see
  785. * https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/smart-home-skill-api-reference#discovery-messages
  786. */
  787. case 'Alexa.Discovery':
  788. handleDiscovery(request.directive, callback);
  789. break;
  790.  
  791. case 'Alexa.PowerController':
  792. handlePowerControl(request.directive, callback);
  793. break;
  794.  
  795. case 'Alexa.ThermostatController':
  796. handleThermostatControl(request.directive, callback);
  797. break;
  798.  
  799. //todo: raspberry keeps connection to fritzbox open and polls current temp and target setpoint every minute, saves it in a cache
  800.  
  801. case 'Alexa':
  802. handleQuery(request.directive, callback);
  803. break;
  804.  
  805. /**
  806. * Received an unexpected message
  807. */
  808. default: {
  809. const errorMessage = `No supported namespace: ${request.directive.header.namespace}`;
  810. log('ERROR', errorMessage);
  811. callback(new Error(errorMessage));
  812. }
  813. }
  814. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement