modular approach, add reconnect
This commit is contained in:
120
src/devicebase.js
Normal file
120
src/devicebase.js
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
|
||||||
|
const TuyaDevice = require('tuyapi');
|
||||||
|
const log4js = require('log4js');
|
||||||
|
|
||||||
|
class DeviceBase {
|
||||||
|
constructor(deviceconfig, mqtt) {
|
||||||
|
// setup logger
|
||||||
|
const loggername = deviceconfig.id ? deviceconfig.id : "undef device"
|
||||||
|
this.logger = log4js.getLogger(loggername);
|
||||||
|
this.logger.level = deviceconfig.loglevel;
|
||||||
|
// check device attributes
|
||||||
|
if (!deviceconfig.id) {
|
||||||
|
this.logger.error("missing attribute 'id' in device config")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!deviceconfig.key) {
|
||||||
|
this.logger.error("missing attribute 'key' in device config")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!deviceconfig.topic) {
|
||||||
|
this.logger.error("missing attribute 'topic' in device config")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// define device vars
|
||||||
|
this.mqtt = mqtt
|
||||||
|
this.topicname = deviceconfig.topic
|
||||||
|
this.deviceid = deviceconfig.id
|
||||||
|
this.devicekey = deviceconfig.key
|
||||||
|
|
||||||
|
this.topic_get = `${deviceconfig.topic}/get`
|
||||||
|
this.topic_set = `${deviceconfig.topic}/set`
|
||||||
|
this.topic_state = `${deviceconfig.topic}/state`
|
||||||
|
|
||||||
|
this.logger.debug(`use topic (all) : ${this.topicname}`)
|
||||||
|
this.logger.debug(`use topic (get) : ${this.topic_get}`)
|
||||||
|
this.logger.debug(`use topic (set) : ${this.topic_set}`)
|
||||||
|
this.logger.debug(`use topic (state) : ${this.topic_state}`)
|
||||||
|
|
||||||
|
this.intervall_refresh = deviceconfig.refresh ? deviceconfig.refresh * 1000 : 10000
|
||||||
|
this.reconnect_timout = deviceconfig.reconnect ? deviceconfig.reconnect * 1000 : 5000
|
||||||
|
|
||||||
|
this.logger.debug(`refresh timer (ms) : ${this.intervall_refresh}`)
|
||||||
|
this.logger.debug(`reconnect timer (ms) : ${this.reconnect_timout}`)
|
||||||
|
|
||||||
|
this.lastdata = {}
|
||||||
|
// call init function
|
||||||
|
this.init()
|
||||||
|
|
||||||
|
// connect to device
|
||||||
|
try {
|
||||||
|
this.device = new TuyaDevice({
|
||||||
|
id: this.deviceid,
|
||||||
|
key: this.devicekey,
|
||||||
|
issueRefreshOnConnect: true,
|
||||||
|
})
|
||||||
|
this.reconnect()
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Cannot connect to ${this.deviceid}`)
|
||||||
|
this.logger.error(error.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.device.on('connected', () => {
|
||||||
|
this.connected = true
|
||||||
|
this.logger.info(`Connected to tuya id: ${this.deviceid}, ip: ${this.device.device.ip}, prefix: ${this.topicname}`)
|
||||||
|
this.startWatcher()
|
||||||
|
// subscribe to topic
|
||||||
|
this.mqtt.subscribe(this.topic_set, (err) => {
|
||||||
|
if (err) {
|
||||||
|
this.logger.error(`Cannot subscribe to ${this.topic_set}`)
|
||||||
|
} else {
|
||||||
|
this.logger.info(`Subscribed to ${this.topic_set}`)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.logger.debug(`publish ${this.totopic_statepic_get}: "online"`)
|
||||||
|
this.mqtt.publish(this.topic_state, "online")
|
||||||
|
});
|
||||||
|
|
||||||
|
this.device.on('disconnected', () => {
|
||||||
|
clearInterval(this.timer)
|
||||||
|
this.logger.info(`Disconnected`)
|
||||||
|
this.logger.debug(`publish ${this.topic_state}: "offline"`)
|
||||||
|
this.mqtt.publish(this.topic_state, "offline")
|
||||||
|
setTimeout(() => this.reconnect(), this.reconnect_timout)
|
||||||
|
this.device.device.ip = undefined // will error without? WHY???
|
||||||
|
});
|
||||||
|
|
||||||
|
this.device.on('error', error => {
|
||||||
|
clearInterval(this.timer)
|
||||||
|
this.logger.debug(`publish ${this.topic_state}: "offline"`)
|
||||||
|
this.mqtt.publish(this.topic_state, "offline")
|
||||||
|
setTimeout(() => this.reconnect(), this.reconnect_timout)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reconnect() {
|
||||||
|
this.device.find().then(el => {
|
||||||
|
if (el) {
|
||||||
|
this.device.connect()
|
||||||
|
} else {
|
||||||
|
this.logger.debug("Reconnect failed: device offline")
|
||||||
|
setTimeout(() => this.reconnect(), this.reconnect_timout)
|
||||||
|
}
|
||||||
|
}).catch(el => {
|
||||||
|
this.logger.debug("Reconnect failed: device timed out")
|
||||||
|
setTimeout(() => this.reconnect(), this.reconnect_timout)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
this.connected = false
|
||||||
|
clearInterval(this.timer)
|
||||||
|
this.device.disconnect()
|
||||||
|
this.logger.info(`Disconnected for id: ${this.deviceid}`)
|
||||||
|
this.reconnect = () => {} // prevent reconnect on exit
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = DeviceBase
|
||||||
@@ -49,7 +49,7 @@ async function main() {
|
|||||||
// Subscribe to a topic
|
// Subscribe to a topic
|
||||||
})
|
})
|
||||||
client.on("reconnect", () => {
|
client.on("reconnect", () => {
|
||||||
loggerInit.info("reconnecting!")
|
loggerInit.info(`Try reconnect to ${mqttserver}`)
|
||||||
})
|
})
|
||||||
client.stream.on('error', (err) => {
|
client.stream.on('error', (err) => {
|
||||||
loggerInit.error('error')
|
loggerInit.error('error')
|
||||||
@@ -61,6 +61,7 @@ async function main() {
|
|||||||
|
|
||||||
if (config.devices){
|
if (config.devices){
|
||||||
config.devices.forEach((device) => {
|
config.devices.forEach((device) => {
|
||||||
|
device.loglevel = config.loglevel
|
||||||
const deviceClassFile = `./modules/${device.type}`
|
const deviceClassFile = `./modules/${device.type}`
|
||||||
loggerInit.info(`Setup device ${device.id}, type: ${device.type}, class:'${deviceClassFile}.js'`)
|
loggerInit.info(`Setup device ${device.id}, type: ${device.type}, class:'${deviceClassFile}.js'`)
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,31 +1,14 @@
|
|||||||
const TuyaDevice = require('tuyapi');
|
/*
|
||||||
const log4js = require('log4js');
|
* Device class definition for Luminea NX-445
|
||||||
|
* https://www.luminea.info/Outdoor-WLAN-Steckdose-kompatibel-zu-Alexa-NX-4458-919.shtml
|
||||||
|
*
|
||||||
|
* by Dennis Gunia (08/2024)
|
||||||
|
*/
|
||||||
|
|
||||||
class Lineplug {
|
const DeviceBase = require('../devicebase')
|
||||||
constructor(deviceconfig, mqtt) {
|
class Lineplug extends DeviceBase {
|
||||||
const loggername = deviceconfig.id ? deviceconfig.id : "undef device"
|
|
||||||
this.logger = log4js.getLogger(loggername);
|
|
||||||
|
|
||||||
if (!deviceconfig.id) {
|
|
||||||
this.logger.error("missing attribute 'id' in device config")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!deviceconfig.key) {
|
|
||||||
this.logger.error("missing attribute 'key' in device config")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!deviceconfig.topic) {
|
|
||||||
this.logger.error("missing attribute 'topic' in device config")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mqtt = mqtt
|
|
||||||
this.topicname = deviceconfig.topic
|
|
||||||
this.deviceid = deviceconfig.id
|
|
||||||
this.topic_get = `${deviceconfig.topic}/get`
|
|
||||||
this.topic_set = `${deviceconfig.topic}/set`
|
|
||||||
this.intervall = deviceconfig.refresh ? deviceconfig.refresh * 1000 : 10000
|
|
||||||
|
|
||||||
|
init() {
|
||||||
this.lastdata = {
|
this.lastdata = {
|
||||||
voltage: 0,
|
voltage: 0,
|
||||||
current: 0,
|
current: 0,
|
||||||
@@ -36,49 +19,27 @@ class Lineplug {
|
|||||||
countdown_1: 0,
|
countdown_1: 0,
|
||||||
random_time: 0,
|
random_time: 0,
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
this.device = new TuyaDevice({
|
|
||||||
id: deviceconfig.id,
|
|
||||||
key: deviceconfig.key,
|
|
||||||
issueRefreshOnConnect: true,
|
|
||||||
ip: deviceconfig.ip,
|
|
||||||
})
|
|
||||||
this.device.find().then(el => {
|
|
||||||
this.device.connect().then(el => {
|
|
||||||
this.logger.info(`Connected to tuya id: ${this.deviceid} @ ${this.topicname}`)
|
|
||||||
this.startWatcher()
|
|
||||||
this.mqtt.subscribe(this.topic_set, (err) => {
|
|
||||||
if (err) {
|
|
||||||
this.logger.error(`Cannot subscribe to ${this.topic_set}`)
|
|
||||||
} else {
|
|
||||||
this.logger.info(`Subscribed to ${this.topic_set}`)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).catch(error => {
|
|
||||||
this.logger.error(`Cannot connect to ${this.deviceid}`)
|
|
||||||
this.logger.error(error.message)
|
|
||||||
});
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Cannot connect to ${this.deviceid}`)
|
|
||||||
this.logger.error(error.message)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startWatcher() {
|
startWatcher() {
|
||||||
// monitoring loop
|
// monitoring loop
|
||||||
this.timer = setInterval(() => this.device.refresh(), 10000)
|
this.timer = setInterval(() => {
|
||||||
|
this.device.refresh()
|
||||||
|
}, this.intervall_refresh)
|
||||||
this.logger.info(`Started watcher for id: ${this.deviceid}`)
|
this.logger.info(`Started watcher for id: ${this.deviceid}`)
|
||||||
this.device.on('data', data => {
|
this.device.on('data', data => {
|
||||||
|
this.logger.debug(`rx data: ${JSON.stringify(data)}`)
|
||||||
this.processData(data)
|
this.processData(data)
|
||||||
});
|
});
|
||||||
this.device.on('dp-refresh', data => {
|
this.device.on('dp-refresh', data => {
|
||||||
|
this.logger.debug(`rx dp-refresh: ${JSON.stringify(data)}`)
|
||||||
this.processData(data)
|
this.processData(data)
|
||||||
});
|
});
|
||||||
// monitor queue
|
// monitor queue
|
||||||
this.mqtt.on('message', (topic, message) => {
|
this.mqtt.on('message', (topic, message) => {
|
||||||
// message is Buffer
|
// message is Buffer
|
||||||
let payload = message.toString()
|
let payload = message.toString()
|
||||||
|
this.logger.debug(`input ${topic}: ${payload}`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const jsonpayload = JSON.parse(payload)
|
const jsonpayload = JSON.parse(payload)
|
||||||
@@ -135,20 +96,19 @@ class Lineplug {
|
|||||||
if (updatedValues.includes('1')) {
|
if (updatedValues.includes('1')) {
|
||||||
this.lastdata.status = dps['1']
|
this.lastdata.status = dps['1']
|
||||||
changed = true
|
changed = true
|
||||||
this.mqtt.publish(this.topic_get, JSON.stringify({
|
const msg = {
|
||||||
value: this.lastdata.status
|
value: this.lastdata.status
|
||||||
}))
|
}
|
||||||
|
this.mqtt.publish(this.topic_get, JSON.stringify(msg))
|
||||||
|
this.logger.debug(`publish ${this.topic_get}: ${JSON.stringify(msg)}`)
|
||||||
}
|
}
|
||||||
if (changed) {
|
if (changed) {
|
||||||
this.mqtt.publish(this.topicname, JSON.stringify(this.lastdata))
|
this.mqtt.publish(this.topicname, JSON.stringify(this.lastdata))
|
||||||
|
this.logger.debug(`publish ${this.topicname}: ${JSON.stringify(this.lastdata)}`)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
|
||||||
clearInterval(this.timer)
|
|
||||||
this.device.disconnect()
|
|
||||||
this.logger.info(`Disconnected for id: ${this.deviceid}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Lineplug
|
module.exports = Lineplug
|
||||||
Reference in New Issue
Block a user