mirror of
https://github.com/lehanspb/tuya-mqtt.git
synced 2025-12-16 17:54:36 +00:00
* Add ability to set individual DPS values via <topic>/dps/<#>/command * Can still use Tuya JSON via <topic>/dps/command * Simple on/off sent to <topic>/command * Brightness sent to <topic>/brightness_command * Simple device type detection (currently only for sockets/switches/dimmers and non-RGB lights, other devices are unknown get DPS 1 on/off in state and all other values accessible via DPS
271 lines
7.7 KiB
JavaScript
271 lines
7.7 KiB
JavaScript
const TuyAPI = require('tuyapi');
|
|
const TuyColor = require('./tuya-color');
|
|
const debug = require('debug')('TuyAPI:device');
|
|
const debugError = require('debug')('TuyAPI:device:error');
|
|
const debugColor = require('debug')('TuyAPI:device:color');
|
|
|
|
/**
|
|
*
|
|
var device = new TuyaDevice({
|
|
id: '03200240600194781244',
|
|
key: 'b8bdebab418f5b55',
|
|
ip: '192.168.178.45',
|
|
version: "3.3",
|
|
type: "<device_type>" <- "switch", "light", "dimmer", etc. Attempts autodetect if not defined
|
|
});
|
|
*/
|
|
|
|
var TuyaDevice = (function () {
|
|
var devices = [];
|
|
var events = {};
|
|
|
|
function checkExisiting(options) {
|
|
var existing = false;
|
|
// Check for existing instance
|
|
devices.forEach(device => {
|
|
if (device.topicLevel == options.topicLevel) {
|
|
existing = device;
|
|
}
|
|
});
|
|
return existing;
|
|
}
|
|
|
|
function deleteDevice(id) {
|
|
devices.forEach((device, key) => {
|
|
if (device.hasOwnProperty("options")) {
|
|
if (id === device.options.id) {
|
|
debug("delete Device", devices[key].toString());
|
|
delete devices[key];
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function TuyaDevice(options) {
|
|
var device = this;
|
|
|
|
// Check for existing instance by matching topicLevel value
|
|
if (existing = checkExisiting(options)) {
|
|
return new Promise((resolve, reject) => {
|
|
resolve({
|
|
status: "connected",
|
|
device: existing
|
|
});
|
|
});
|
|
}
|
|
|
|
if (!(this instanceof TuyaDevice)) {
|
|
return new TuyaDevice(options);
|
|
}
|
|
|
|
this.options = options;
|
|
|
|
if (this.options.name) {
|
|
this.topicLevel = this.options.name.toLowerCase().replace(/ /g,"_");
|
|
} else {
|
|
this.topicLevel = this.options.id;
|
|
}
|
|
|
|
Object.defineProperty(this, 'device', {
|
|
value: new TuyAPI(JSON.parse(JSON.stringify(this.options)))
|
|
});
|
|
|
|
this.device.on('data', data => {
|
|
if (typeof data == "string") {
|
|
debugError('Data from device not encrypted:', data.replace(/[^a-zA-Z0-9 ]/g, ""));
|
|
} else {
|
|
debug('Data from device:', data);
|
|
device.triggerAll('data', data);
|
|
}
|
|
});
|
|
|
|
devices.push(this);
|
|
|
|
// Find device on network
|
|
debug("Search device in network");
|
|
this.find().then(() => {
|
|
debug("Device found in network");
|
|
// Connect to device
|
|
this.device.connect();
|
|
});
|
|
|
|
/**
|
|
* @return Promise to wait for connection
|
|
*/
|
|
return new Promise((resolve, reject) => {
|
|
this.device.on('connected', () => {
|
|
device.triggerAll('connected');
|
|
device.connected = true;
|
|
debug('Connected to device.', device.toString());
|
|
resolve({
|
|
status: "connected",
|
|
device: this
|
|
});
|
|
});
|
|
this.device.on('disconnected', () => {
|
|
device.triggerAll('disconnected');
|
|
device.connected = false;
|
|
debug('Disconnected from device.', device.toString());
|
|
deleteDevice(options.id);
|
|
return reject({
|
|
status: "disconnect",
|
|
device: null
|
|
});
|
|
});
|
|
|
|
this.device.on('error', (err) => {
|
|
debugError(err);
|
|
device.triggerAll('error', err);
|
|
return reject({
|
|
error: err,
|
|
device: this
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
TuyaDevice.prototype.toString = function () {
|
|
return this.name + " (" + this.options.ip + ", " + this.options.id + ", " + this.options.key + ")";
|
|
}
|
|
|
|
TuyaDevice.prototype.triggerAll = function (name, argument) {
|
|
var device = this;
|
|
var e = events[name] || [];
|
|
e.forEach(event => {
|
|
event.call(device, argument);
|
|
});
|
|
}
|
|
|
|
TuyaDevice.prototype.on = function (name, callback) {
|
|
if (!this.connected) return;
|
|
var device = this;
|
|
this.device.on(name, function () {
|
|
callback.apply(device, arguments);
|
|
});
|
|
}
|
|
|
|
TuyaDevice.prototype.find = function () {
|
|
return this.device.find();
|
|
}
|
|
|
|
TuyaDevice.prototype.get = function () {
|
|
return this.device.get();
|
|
}
|
|
|
|
TuyaDevice.prototype.set = function (options) {
|
|
debug('set:', options);
|
|
return new Promise((resolve, reject) => {
|
|
this.device.set(options).then((result) => {
|
|
this.get().then(() => {
|
|
debug("Set completed ");
|
|
resolve(result);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
TuyaDevice.prototype.switch = function (newStatus, callback) {
|
|
if (!this.connected) return;
|
|
newStatus = newStatus.toLowerCase();
|
|
if (newStatus == "on") {
|
|
return this.switchOn(callback);
|
|
}
|
|
if (newStatus == "off") {
|
|
return this.switchOff(callback);
|
|
}
|
|
if (newStatus == "toggle") {
|
|
return this.toggle(callback);
|
|
}
|
|
}
|
|
|
|
TuyaDevice.prototype.switchOn = function () {
|
|
if (!this.connected) return;
|
|
debug("switch -> ON");
|
|
|
|
return this.set({
|
|
set: true
|
|
});
|
|
}
|
|
|
|
TuyaDevice.prototype.switchOff = function () {
|
|
if (!this.connected) return;
|
|
debug("switch -> OFF");
|
|
|
|
return this.set({
|
|
set: false
|
|
});
|
|
}
|
|
|
|
TuyaDevice.prototype.toggle = function () {
|
|
if (!this.connected) return;
|
|
return new Promise((resolve, reject) => {
|
|
this.get().then((status) => {
|
|
debug("toogle state", status);
|
|
this.set({
|
|
set: !status
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
TuyaDevice.prototype.schema = function(obj){
|
|
return this.get(obj).then((status) => {
|
|
debug("get", obj);
|
|
});
|
|
}
|
|
|
|
TuyaDevice.prototype.setColor = function (hexColor) {
|
|
if (!this.connected) return;
|
|
debugColor("Set color to: ", hexColor);
|
|
var tuya = this.device;
|
|
var color = new TuyColor(tuya);
|
|
var dps = color.setColor(hexColor);
|
|
debugColor("dps values:", dps);
|
|
|
|
return this.set({
|
|
multiple: true,
|
|
data: dps
|
|
});
|
|
}
|
|
|
|
TuyaDevice.prototype.connect = function (callback) {
|
|
debug("Connect to TuyAPI Device");
|
|
return this.device.connect(callback);
|
|
}
|
|
|
|
TuyaDevice.prototype.disconnect = function (callback) {
|
|
debug("Disconnect from TuyAPI Device");
|
|
return this.device.disconnect(callback);
|
|
}
|
|
|
|
Object.defineProperty(TuyaDevice, 'devices', {
|
|
value: devices
|
|
});
|
|
|
|
TuyaDevice.connectAll = function () {
|
|
devices.forEach(device => {
|
|
device.connect();
|
|
});
|
|
}
|
|
|
|
TuyaDevice.disconnectAll = function () {
|
|
devices.forEach(device => {
|
|
device.disconnect();
|
|
});
|
|
}
|
|
|
|
TuyaDevice.onAll = function (name, callback) {
|
|
if (events[name] == undefined) {
|
|
events[name] = [];
|
|
}
|
|
events[name].push(callback);
|
|
devices.forEach(device => {
|
|
device.triggerAll(name);
|
|
});
|
|
}
|
|
|
|
return TuyaDevice;
|
|
}());
|
|
|
|
module.exports = TuyaDevice;
|