@di-zed/yandex-smart-home
Version:
The Yandex Smart Home skills for the different device types.
221 lines (220 loc) • 11 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const configProvider_1 = __importDefault(require("../../providers/configProvider"));
const mqttProvider_1 = __importDefault(require("../../providers/mqttProvider"));
const deviceService_1 = __importDefault(require("../../services/deviceService"));
const mqttService_1 = __importDefault(require("../../services/mqttService"));
const topicService_1 = __importDefault(require("../../services/topicService"));
const deviceHelper_1 = __importDefault(require("../../helpers/deviceHelper"));
const restUserDevicesAfterEvent_1 = __importDefault(require("../../events/restUserDevicesAfterEvent"));
const appError_1 = __importDefault(require("../../errors/appError"));
/**
* REST User Controller.
*/
class RestUserController {
/**
* POST Method.
* Notification of unlinked accounts.
* https://yandex.ru/dev/dialogs/smart-home/doc/reference/unlink.html?lang=en
*
* @param req
* @param res
* @returns Response
*/
unlink(req, res) {
return __awaiter(this, void 0, void 0, function* () {
console.log(res.__("The user's provider account (#%s) and Yandex account (#%s) were unlinked (the link between them was deleted by the user).", String(req.currentUser.id), String(req.currentClient.id)));
const callbackRestUserUnlinkAction = configProvider_1.default.getConfigOption('callbackRestUserUnlinkAction');
if (typeof callbackRestUserUnlinkAction === 'function') {
yield callbackRestUserUnlinkAction(req, res);
}
return res.status(200).json({
request_id: req.requestId,
});
});
}
/**
* GET Method.
* Information about user devices.
* https://yandex.ru/dev/dialogs/smart-home/doc/reference/get-devices.html?lang=en
*
* @param req
* @param res
* @returns Response
*/
devices(req, res) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
const devices = yield deviceService_1.default.getUserDevices(req.currentUser.id);
const updatedDevices = [];
const response = {
request_id: req.requestId,
payload: {
user_id: String(req.currentUser.id),
devices: [],
},
};
for (const device of devices) {
const updatedDevice = yield deviceService_1.default.updateUserDevice(req.currentUser, device);
updatedDevices.push(structuredClone(updatedDevice));
(_a = updatedDevice.capabilities) === null || _a === void 0 ? void 0 : _a.forEach((capability) => {
delete capability.state;
});
(_b = updatedDevice.properties) === null || _b === void 0 ? void 0 : _b.forEach((property) => {
delete property.state;
});
response.payload.devices.push(updatedDevice);
}
restUserDevicesAfterEvent_1.default.execute(req.currentUser, updatedDevices).catch((err) => {
console.log('ERROR! REST User Devices After Event.', err instanceof Error ? err.message : err);
});
return res.status(200).json(response);
});
}
/**
* POST Method.
* Information about the states of user devices.
* https://yandex.ru/dev/dialogs/smart-home/doc/reference/post-devices-query.html?lang=en
*
* @param req
* @param res
* @param next
* @returns Response
*/
devicesQuery(req, res, next) {
return __awaiter(this, void 0, void 0, function* () {
if (typeof req.body.devices !== 'object') {
return next(new appError_1.default(res.__('The parameter "%s" is required.', 'devices'), 400));
}
const response = {
request_id: req.requestId,
payload: {
devices: JSON.parse(JSON.stringify(req.body.devices)),
},
};
for (const payloadDevice of response.payload.devices) {
const userDevice = yield deviceService_1.default.getUserDeviceById(req.currentUser.id, payloadDevice.id);
if (userDevice === undefined) {
payloadDevice.error_code = 'DEVICE_UNREACHABLE'; // DEVICE_NOT_FOUND
payloadDevice.error_message = res.__('The device "%s" can not be found.', payloadDevice.id);
continue;
}
const updatedDevice = yield deviceService_1.default.updateUserDevice(req.currentUser, userDevice);
if (updatedDevice.error_code) {
payloadDevice.error_code = updatedDevice.error_code;
payloadDevice.error_message = updatedDevice.error_message || res.__('Something went wrong!');
continue;
}
payloadDevice.capabilities = [];
for (const capability of updatedDevice.capabilities || []) {
payloadDevice.capabilities.push({
type: capability.type,
state: capability.state,
});
}
payloadDevice.properties = [];
for (const property of updatedDevice.properties || []) {
payloadDevice.properties.push({
type: property.type,
state: property.state,
});
}
}
return res.status(200).json(response);
});
}
/**
* POST Method.
* Change device state.
* https://yandex.ru/dev/dialogs/smart-home/doc/reference/post-action.html?lang=en
*
* @param req
* @param res
* @param next
* @returns Response | void
*/
devicesAction(req, res, next) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
if (typeof req.body.payload !== 'object' || typeof req.body.payload.devices !== 'object') {
return next(new appError_1.default(res.__('The parameter "%s" is required.', 'devices'), 400));
}
const response = {
request_id: req.requestId,
payload: {
devices: JSON.parse(JSON.stringify(req.body.payload.devices)),
},
};
for (const payloadDevice of response.payload.devices) {
const userDevice = yield deviceService_1.default.getUserDeviceById(req.currentUser.id, payloadDevice.id);
if (userDevice === undefined) {
continue;
}
const updatedDevice = yield deviceService_1.default.updateUserDevice(req.currentUser, userDevice);
const payloadCapabilities = payloadDevice.capabilities || [];
for (const payloadCapability of payloadCapabilities) {
let actionResult = {
status: 'ERROR',
error_code: 'DEVICE_UNREACHABLE', // INVALID_ACTION
error_message: res.__('Capability "%s" for the device "%s" can not be changed.', payloadCapability.type, userDevice.id),
};
const capabilityState = payloadCapability.state;
if (updatedDevice.error_code) {
delete capabilityState.value;
actionResult.error_message = updatedDevice.error_message || actionResult.error_message;
capabilityState.action_result = actionResult;
continue;
}
const topicNames = yield mqttService_1.default.getTopicNames({
user: req.currentUser,
deviceId: userDevice.id,
capabilityType: payloadCapability.type,
capabilityStateInstance: capabilityState.instance,
});
try {
if (topicNames.commandTopic) {
let value = capabilityState.value;
let topicData = undefined;
if (updatedDevice.type) {
topicData = yield mqttService_1.default.getCommandTopicData(topicNames.commandTopic, updatedDevice.type, {
capabilityType: payloadCapability.type,
capabilityStateInstance: capabilityState.instance,
});
}
if (topicData !== undefined && topicData.capabilityType === 'devices.capabilities.range') {
const rangeState = JSON.parse(JSON.stringify(capabilityState));
if (rangeState.relative) {
const deviceCapability = deviceHelper_1.default.getDeviceCapability(updatedDevice, payloadCapability.type, capabilityState.instance);
if (deviceCapability !== undefined) {
value += ((_a = deviceCapability.state) === null || _a === void 0 ? void 0 : _a.value) || 0;
}
}
}
yield mqttProvider_1.default.publish(topicNames.commandTopic, yield topicService_1.default.convertAliceValueToMqttMessage(value, topicData));
actionResult = { status: 'DONE' };
}
}
catch (err) {
console.log('ERROR! User Devices Action.', { err, actionResult });
}
delete capabilityState.value;
capabilityState.action_result = actionResult;
}
}
return res.status(200).json(response);
});
}
}
exports.default = RestUserController;