Skip to content
This repository has been archived by the owner on Jul 10, 2024. It is now read-only.

Concurrent user issue #8

Merged
merged 9 commits into from
Mar 28, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
],
"linebreak-style": [
"error",
"windows"
"linux"
],
"quotes": [
"error",
Expand Down
1 change: 1 addition & 0 deletions aws/ThermostatRepository.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class ThermostatRepository {

let response = await this.client.get(params).promise();
if (response.Item) {
console.log(`Found thermostat for user ${userId} with username ${response.Item.options.username}`);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'console' is not defined.
'template literal syntax' is only available in ES6 (use 'esversion: 6').

return response.Item;
}
return null;
Expand Down
38 changes: 27 additions & 11 deletions aws/lambda.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,25 @@ module.change_code = 0;

let app = new alexa.app('boiler');

const controlService = (userId) => {
let context = { userId };
const controlService = (request) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'arrow function syntax (=>)' is only available in ES6 (use 'esversion: 6').
'const' is available in ES6 (use 'esversion: 6') or Mozilla JS extensions (use moz).

console.log('*******************************');
console.log(`UserId: ${request.userId}`);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'template literal syntax' is only available in ES6 (use 'esversion: 6').

console.log('Data');
console.log(request.data);
console.log('Context');
console.log(request.context);
if (request.data.context) {
console.log(`Deep UserId: ${request.data.context.System.user.userId}`);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'template literal syntax' is only available in ES6 (use 'esversion: 6').

}
console.log('*******************************');

let userId = request.userId || request.data.session.user.userId;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'let' is available in ES6 (use 'esversion: 6') or Mozilla JS extensions (use moz).

let source = 'user';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'let' is available in ES6 (use 'esversion: 6') or Mozilla JS extensions (use moz).

if (!request.data.context) {
source = 'callback';
}
let context = { userId: userId, source: source };

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'let' is available in ES6 (use 'esversion: 6') or Mozilla JS extensions (use moz).

console.log(`Creating context for source: ${context.source}, user: ${context.userId}...`);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'template literal syntax' is only available in ES6 (use 'esversion: 6').

let repository;
if (process.env.THERMOSTAT_REPOSITORY === 'dynamodb') {
repository = new DynamodbThermostatRepository();
Expand Down Expand Up @@ -44,7 +61,7 @@ const say = (response, messages) => {

app.launch(async (request, response) => {
console.log('Launching...');
let service = controlService(request.userId);
let service = controlService(request);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'let' is available in ES6 (use 'esversion: 6') or Mozilla JS extensions (use moz).

try {
let messages = await service.launch();
say(response, messages);
Expand All @@ -57,7 +74,7 @@ app.launch(async (request, response) => {
app.intent('TempIntent', {
'utterances': ['what the temperature is', 'the temperature', 'how hot it is']
}, async (request, response) => {
let service = controlService(request.userId);
let service = controlService(request);
try {
let messages = await service.status();
say(response, messages);
Expand All @@ -70,7 +87,7 @@ app.intent('TempIntent', {
app.intent('TurnUpIntent', {
'utterances': ['to increase', 'to turn up', 'set warmer', 'set higher']
}, async (request, response) => {
let service = controlService(request.userId);
let service = controlService(request);
try {
let messages = await service.turnUp();
say(response, messages);
Expand All @@ -83,7 +100,7 @@ app.intent('TurnUpIntent', {
app.intent('TurnDownIntent', {
'utterances': ['to decrease', 'to turn down', 'set cooler', 'set lower']
}, async (request, response) => {
let service = controlService(request.userId);
let service = controlService(request);
try {
let messages = await service.turnDown();
say(response, messages);
Expand All @@ -99,7 +116,7 @@ app.intent('SetTempIntent', {
},
'utterances': ['to set to {temp} degrees', 'to set the temperature to {temp} degrees', 'to set the temp to {temp} degrees']
}, async (request, response) => {
let service = controlService(request.userId);
let service = controlService(request);
try {
let messages = await service.setTemperature(request.slot('temp'), request.slot('duration'));
say(response, messages);
Expand All @@ -114,12 +131,11 @@ app.intent('TurnIntent', {
'onoff': 'ONOFF'
},
'utterances': ['to turn {onoff}', 'to turn heating {onoff}', 'to turn the heating {onoff}']
}, async (request, response) => {
}, async (request, response) => {
let onOff = request.slot('onoff');
let duration = request.slot('duration');
// this could be a callback from a step function
let userId = request.userId || request.data.session.user.userId;
let service = controlService(userId);
let service = controlService(request);
try {
let messages = await service.turn(onOff, duration);
say(response, messages);
Expand All @@ -143,7 +159,7 @@ app.intent('AMAZON.StopIntent', {
'slots': {},
'utterances': []
}, async (request, response) => {
let service = controlService(request.userId);
let service = controlService(request);
try {
let messages = await service.turn('off');
say(response, messages);
Expand Down
193 changes: 108 additions & 85 deletions core/ControlService.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,18 @@ class ControlService {

async obtainThermostat() {
let thermostat = await this._thermostatRepository.find(this._context.userId);
if (!thermostat) {
thermostat = await this._thermostatRepository.find('template');
if (thermostat) {
thermostat.userId = this._context.userId;
}
else {
thermostat = { userId: this._context.userId, executionId: null };
}
await this._thermostatRepository.add(thermostat);
if (thermostat) {
return thermostat;
}

thermostat = await this._thermostatRepository.find('template');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolon.

if (thermostat) {
thermostat.userId = this._context.userId;
}
else {
thermostat = { userId: this._context.userId, executionId: null };
}
await this._thermostatRepository.add(thermostat);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected an assignment or function call and instead saw an expression.
Missing semicolon.

return thermostat;
}

Expand All @@ -48,27 +50,35 @@ class ControlService {

async launch() {
let client = await this.login();
if (await client.online()) {
return 'Thermostat is online';
} else {
return 'Sorry, the thermostat is offline at the moment.';
try {
if (await client.online()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected ')' to match '(' from line 54 and instead saw 'client'.
Expected an assignment or function call and instead saw an expression.
Expected an identifier and instead saw ')'.
Expected an identifier and instead saw '.'.
Missing semicolon.

return 'Thermostat is online';
} else {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected an assignment or function call and instead saw an expression.
Expected an identifier and instead saw 'else'.
Missing semicolon.

return 'Sorry, the thermostat is offline at the moment.';
}
} finally {
await client.logout();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected an assignment or function call and instead saw an expression.
Missing semicolon.

}
}

async status() {
console.log('Requesting status...');
let client = await this.login();
await this.verifyOnline(client);
let device = await client.device();
this.verifyContactable(device);

let messages = [];
messages.push(`The current temperature is ${this.speakTemperature(device.currentTemperature)} degrees.`);
messages.push(`The target is ${this.speakTemperature(device.targetTemperature)} degrees.`);
await this.determineIfHolding(device, messages);

this.logStatus(device);
return messages;
try {
await this.verifyOnline(client);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected an assignment or function call and instead saw an expression.
Missing semicolon.

let device = await client.device();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'let' is available in ES6 (use 'esversion: 6') or Mozilla JS extensions (use moz).
Missing semicolon.

this.verifyContactable(device);

let messages = [];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'let' is available in ES6 (use 'esversion: 6') or Mozilla JS extensions (use moz).

messages.push(`The current temperature is ${this.speakTemperature(device.currentTemperature)} degrees.`);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'template literal syntax' is only available in ES6 (use 'esversion: 6').

messages.push(`The target is ${this.speakTemperature(device.targetTemperature)} degrees.`);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'template literal syntax' is only available in ES6 (use 'esversion: 6').

await this.determineIfHolding(device, messages);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected an assignment or function call and instead saw an expression.
Too many errors. (33% scanned).


this.logStatus(device);
return messages;
} finally {
await client.logout();
}
}

async determineIfHolding(device, messages, qualifier = '') {
Expand All @@ -90,51 +100,61 @@ class ControlService {
async turnUp() {
console.log('Turning up...');
let client = await this.login();
await this.verifyOnline(client);
let device = await client.device();
this.verifyContactable(device);

if (device.status == 'on') {
throw 'The heating is already on.';
}
try {
await this.verifyOnline(client);
let device = await client.device();
this.verifyContactable(device);

let t = device.targetTemperature + 0.5;
await client.setTemperature(t);
let updatedDevice = await client.device();
if (device.status == 'on') {
throw 'The heating is already on.';
}

let t = device.targetTemperature + 0.5;
await client.setTemperature(t);
let updatedDevice = await client.device();

let messages = [];
messages.push(`The target temperature is now ${this.speakTemperature(updatedDevice.targetTemperature)} degrees.`);
await this.determineIfHolding(updatedDevice, messages, 'now');
let messages = [];
messages.push(`The target temperature is now ${this.speakTemperature(updatedDevice.targetTemperature)} degrees.`);
await this.determineIfHolding(updatedDevice, messages, 'now');

this.logStatus(device);
return messages;
this.logStatus(device);
return messages;
} finally {
await client.logout();
}
}

async turnDown() {
console.log('Turning down...');
let client = await this.login();
await this.verifyOnline(client);
let device = await client.device();
this.verifyContactable(device);

let t = device.targetTemperature - 1.0;
await client.setTemperature(t);
let updatedDevice = await client.device();

let messages = [];
messages.push(`The target temperature is now ${this.speakTemperature(updatedDevice.targetTemperature)} degrees.`);
await this.determineIfHolding(updatedDevice, messages, 'still');

this.logStatus(updatedDevice);
return messages;
try {
await this.verifyOnline(client);
let device = await client.device();
this.verifyContactable(device);

let t = device.targetTemperature - 1.0;
await client.setTemperature(t);
let updatedDevice = await client.device();

let messages = [];
messages.push(`The target temperature is now ${this.speakTemperature(updatedDevice.targetTemperature)} degrees.`);
await this.determineIfHolding(updatedDevice, messages, 'still');

this.logStatus(updatedDevice);
return messages;
} finally {
await client.logout();
}
}

async turn(onOff, duration) {
console.log(`Turning ${onOff}...`);

let t = process.env.DEFAULT_ON_TEMP || '20';
let thermostat = await this.obtainThermostat();
let t = thermostat.defaultOnTemp;
if (onOff === 'off') {
t = process.env.DEFAULT_OFF_TEMP || '14';
t = thermostat.defaultOffTemp;
}

return this.setTemperature(t, duration);
Expand All @@ -143,41 +163,45 @@ class ControlService {
async setTemperature(targetTemperature, forDuration) {
console.log(`Setting temperature to ${targetTemperature}...`);
let client = await this.login();
await this.verifyOnline(client);
let device = await client.device();
this.verifyContactable(device);

await client.setTemperature(targetTemperature);
let updatedDevice = await client.device();

let messages = [];
messages.push(`The target temperature is now ${this.speakTemperature(updatedDevice.targetTemperature)} degrees.`);
this.logStatus(updatedDevice);

let duration = forDuration || process.env.DEFAULT_DURATION;

let intent = await this._holdStrategy.holdIfRequiredFor(duration);
return messages.concat(this.summarize(intent, updatedDevice));
try {
await this.verifyOnline(client);
let device = await client.device();
this.verifyContactable(device);

await client.setTemperature(targetTemperature);
let updatedDevice = await client.device();

let messages = [];
messages.push(`The target temperature is now ${this.speakTemperature(updatedDevice.targetTemperature)} degrees.`);
this.logStatus(updatedDevice);

if (this._context.source === 'user') {
let thermostat = await this.obtainThermostat();
let duration = forDuration || thermostat.defaultDuration;
let intent = await this._holdStrategy.holdIfRequiredFor(duration);
return messages.concat(this.summarize(intent, updatedDevice));
}
return messages;
} finally {
await client.logout();
}
}

summarize(intent, updatedDevice) {
let messages = [];
if (intent.holding) {
let durationText = this.speakDuration(intent.duration);
console.log(`Holding for ${durationText} {${intent.executionId}}`);
if (!intent.holding) {
if (updatedDevice.status == 'on') {
messages.push(`The heating is now on and will turn off in ${durationText}`);
}
else {
messages.push(`The heating will turn off in ${durationText}`);
return ['The heating is now on.'];
}
return [];
}
else {
if (updatedDevice.status == 'on') {
messages.push('The heating is now on.');
}

let durationText = this.speakDuration(intent.duration);
console.log(`Holding for ${durationText} {${intent.executionId}}`);
if (updatedDevice.status == 'on') {
return [`The heating is now on and will turn off in ${durationText}`];
}
return messages;

return [`The heating will turn off in ${durationText}`];
}

logStatus(device) {
Expand All @@ -193,9 +217,8 @@ class ControlService {
}

speakTemperature(temp) {
let t = parseFloat(temp);
if (parseFloat(t.toFixed(0)) != t) return t.toFixed(1);
else return t.toFixed(0);
if (parseFloat(temp.toFixed(0)) != temp) return temp.toFixed(1);
else return temp.toFixed(0);
}
}

Expand Down
5 changes: 4 additions & 1 deletion core/ThermostatRepository.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ class ThermostatRepository {
options: {
username: process.env.USERNAME,
password: process.env.PASSWORD
}
},
defaultOnTemp: parseFloat(process.env.DEFAULT_ON_TEMP || '20'),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'process' is not defined.

defaultOffTemp: parseFloat(process.env.DEFAULT_OFF_TEMP || '14'),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'process' is not defined.

defaultDuration: process.env.DEFAULT_DURATION || 'PT1H'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'process' is not defined.

};
}

Expand Down
Loading