-
-
Notifications
You must be signed in to change notification settings - Fork 408
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
670 additions
and
351 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
const Valetudo = require("./Valetudo"); | ||
const Valetudo = require("./lib/Valetudo"); | ||
new Valetudo(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
const fs = require("fs"); | ||
const Tools = require("./Tools"); | ||
const path = require("path"); | ||
|
||
/** | ||
* @constructor | ||
*/ | ||
const Configuration = function() { | ||
this.location = process.env.VALETUDO_CONFIG ? process.env.VALETUDO_CONFIG : "/mnt/data/valetudo/config.json"; | ||
this.settings = { | ||
"spots": [], | ||
"areas": [], | ||
"mqtt" : { | ||
enabled: false, | ||
identifier: "rockrobo", | ||
broker_url: "mqtt://foobar.example" | ||
} | ||
}; | ||
|
||
/* load an existing configuration file. if it is not present, create it using the default configuration */ | ||
if(fs.existsSync(this.location)) { | ||
console.log("Loading configuration file:", this.location); | ||
|
||
try { | ||
this.settings = JSON.parse(fs.readFileSync(this.location)); | ||
} catch(e) { | ||
//TODO: handle this | ||
console.error("Invalid configuration file!"); | ||
throw e; | ||
} | ||
} else { | ||
console.log("No configuration file present. Creating one at:", this.location); | ||
Tools.MK_DIR_PATH(path.dirname(this.location)); | ||
this.persist(); | ||
} | ||
}; | ||
|
||
|
||
/** | ||
* | ||
* @param key {string} | ||
* @returns {*} | ||
*/ | ||
Configuration.prototype.get = function(key) { | ||
return this.settings[key]; | ||
}; | ||
|
||
Configuration.prototype.getAll = function() { | ||
return this.settings; | ||
}; | ||
|
||
/** | ||
* | ||
* @param key {string} | ||
* @param val {string} | ||
*/ | ||
Configuration.prototype.set = function(key, val) { | ||
this.settings[key] = val; | ||
|
||
this.persist(); | ||
}; | ||
|
||
Configuration.prototype.persist = function() { | ||
fs.writeFile(this.location, JSON.stringify(this.settings, null, 2), (err) => { | ||
if (err) { | ||
console.error(err); | ||
} | ||
}); | ||
}; | ||
|
||
module.exports = Configuration; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
const mqtt = require("mqtt"); | ||
|
||
|
||
const COMMANDS = { | ||
turn_on: "turn_on", | ||
return_to_base: "return_to_base", | ||
stop: "stop", | ||
clean_spot: "clean_spot", | ||
locate: "locate", | ||
start_pause: "start_pause", | ||
set_fan_speed: "set_fan_speed" | ||
}; | ||
|
||
//TODO: since this is also displayed in the UI it should be moved somewhere else | ||
const FAN_SPEEDS = { | ||
min: 38, | ||
medium: 60, | ||
high: 75, | ||
max: 100 | ||
}; | ||
|
||
|
||
|
||
/** | ||
* | ||
* @param options {object} | ||
* @param options.vacuum {Vacuum} | ||
* @param options.brokerURL {string} | ||
* @param options.identifier {string} | ||
* @constructor | ||
*/ | ||
const MqttClient = function(options) { | ||
this.vacuum = options.vacuum; | ||
this.brokerURL = options.brokerURL; | ||
this.identifier = options.identifier || "rockrobo"; | ||
|
||
this.topics = { | ||
command: "valetudo/" + this.identifier + "/command", | ||
set_fan_speed: "valetudo/" + this.identifier + "/set_fan_speed", | ||
send_command: "valetudo/" + this.identifier + "/custom_command", | ||
state: "valetudo/" + this.identifier + "/state", | ||
homeassistant_autoconf: "homeassistant/vacuum/valetudo_" + this.identifier + "/config" | ||
}; | ||
|
||
this.connect(); | ||
this.updateStateTopic(); | ||
}; | ||
|
||
MqttClient.prototype.connect = function() { | ||
if(!this.client || (this.client && this.client.connected === false && this.client.reconnecting === false)) { | ||
this.client = mqtt.connect(this.brokerURL); | ||
|
||
this.client.on("connect", () => { | ||
this.client.subscribe([ | ||
this.topics.command, | ||
this.topics.set_fan_speed | ||
], err => { | ||
if(!err) { | ||
this.client.publish(this.topics.homeassistant_autoconf, JSON.stringify({ | ||
name: this.identifier, | ||
supported_features: [ | ||
"turn_on", | ||
"pause", | ||
"stop", | ||
"return_home", | ||
"battery", | ||
"status", | ||
"locate", | ||
"clean_spot", | ||
"fan_speed", | ||
"send_command" | ||
], | ||
command_topic: this.topics.command, | ||
battery_level_topic: this.topics.state, | ||
battery_level_template: "{{ value_json.battery_level }}", | ||
charging_topic: this.topics.state, | ||
charging_template: "{{value_json.charging}}", | ||
cleaning_topic: this.topics.state, | ||
cleaning_template: "{{value_json.cleaning}}", | ||
docked_topic: this.topics.state, | ||
docked_template: "{{value_json.docked}}", | ||
error_topic: this.topics.state, | ||
error_template: "{{value_json.error}}", | ||
fan_speed_topic: this.topics.state, | ||
fan_speed_template: "{{ value_json.fan_speed }}", | ||
set_fan_speed_topic: this.topics.set_fan_speed, | ||
fan_speed_list: [ | ||
"min", | ||
"medium", | ||
"high", | ||
"max" | ||
], | ||
send_command_topic: this.topics.send_command | ||
|
||
}), { | ||
retain: true | ||
}) | ||
} else { | ||
//TODO: needs more error handling | ||
console.error(err); | ||
} | ||
}); | ||
}); | ||
|
||
this.client.on("message", (topic, message) => { | ||
this.handleCommand(topic, message.toString()); | ||
}) | ||
} | ||
}; | ||
|
||
MqttClient.prototype.updateStateTopic = function() { | ||
if(this.stateUpdateTimeout) { | ||
clearTimeout(this.stateUpdateTimeout); | ||
} | ||
|
||
if(this.client && this.client.connected === true) { | ||
this.vacuum.getCurrentStatus((err, res) => { | ||
if(!err) { | ||
var response = {}; | ||
|
||
response.battery_level = res.battery; | ||
response.docked = [8,14].indexOf(res.state) !== -1; | ||
response.cleaning = res.in_cleaning === 1; | ||
response.charging = res.state === 8; | ||
|
||
switch(res.fan_power) { | ||
case FAN_SPEEDS.min: | ||
response.fan_speed = "min"; | ||
break; | ||
case FAN_SPEEDS.medium: | ||
response.fan_speed = "medium"; | ||
break; | ||
case FAN_SPEEDS.high: | ||
response.fan_speed = "high"; | ||
break; | ||
case FAN_SPEEDS.max: | ||
response.fan_speed = "max"; | ||
break; | ||
default: | ||
response.fan_speed = res.fan_power; | ||
} | ||
|
||
if(res.error_code !== 0) { | ||
response.error = res.human_error; | ||
} | ||
|
||
this.client.publish(this.topics.state, JSON.stringify(response)); | ||
this.stateUpdateTimeout = setTimeout(() => { | ||
this.updateStateTopic() | ||
}, 10000); | ||
} else { | ||
console.error(err); | ||
this.stateUpdateTimeout = setTimeout(() => { | ||
this.updateStateTopic() | ||
}, 30000); | ||
} | ||
}) | ||
} else { | ||
this.stateUpdateTimeout = setTimeout(() => { | ||
this.updateStateTopic() | ||
}, 30000); | ||
} | ||
}; | ||
|
||
/** | ||
* @param topic {string} | ||
* @param command {string} | ||
*/ | ||
MqttClient.prototype.handleCommand = function(topic, command) { | ||
var param; | ||
if(topic === this.topics.set_fan_speed) { | ||
param = command; | ||
command = COMMANDS.set_fan_speed; | ||
} | ||
|
||
switch(command) { //TODO: error handling | ||
case COMMANDS.turn_on: | ||
this.vacuum.startCleaning(() => { | ||
this.updateStateTopic(); | ||
}); | ||
break; | ||
case COMMANDS.stop: | ||
this.vacuum.stopCleaning(() => { | ||
this.updateStateTopic(); | ||
}); | ||
break; | ||
case COMMANDS.return_to_base: | ||
this.vacuum.stopCleaning(() => { | ||
this.vacuum.driveHome(() => { | ||
this.updateStateTopic(); | ||
}); | ||
}); | ||
break; | ||
case COMMANDS.clean_spot: | ||
this.vacuum.spotClean(() => { | ||
this.updateStateTopic(); | ||
}); | ||
break; | ||
case COMMANDS.locate: | ||
this.vacuum.findRobot(() => { | ||
this.updateStateTopic(); | ||
}); | ||
break; | ||
case COMMANDS.start_pause: | ||
this.vacuum.getCurrentStatus((err, res) => { | ||
if(!err) { | ||
if(res.in_cleaning === 1 && [5,11,17].indexOf(res.state) !== -1) { | ||
this.vacuum.pauseCleaning(() => { | ||
this.updateStateTopic(); | ||
}); | ||
} else { | ||
this.vacuum.startCleaning(() => { | ||
this.updateStateTopic(); | ||
}); | ||
} | ||
} | ||
}); | ||
break; | ||
case COMMANDS.set_fan_speed: | ||
this.vacuum.setFanSpeed(FAN_SPEEDS[param], () => { | ||
this.updateStateTopic(); | ||
}); | ||
break; | ||
default: | ||
this.updateStateTopic(); | ||
} | ||
|
||
}; | ||
|
||
module.exports = MqttClient; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
const fs = require("fs"); | ||
const path = require("path"); | ||
|
||
const Tools = { | ||
MK_DIR_PATH : function(filepath) { | ||
var dirname = path.dirname(filepath); | ||
if (!fs.existsSync(dirname)) { | ||
Tools.MK_DIR_PATH(dirname); | ||
} | ||
if (!fs.existsSync(filepath)) { | ||
fs.mkdirSync(filepath); | ||
} | ||
} | ||
}; | ||
|
||
module.exports = Tools; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
Oops, something went wrong.
69be9df
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mqtt implementation supports login?
my broker is secured with a username and password...
69be9df
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@reaper7 I guess mqtt://user:password@host should work, although I haven't tested that. Can you try and report back?
69be9df
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will try to check this solution ASAP
Where I should define the server?
69be9df
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The config file is stored at
/mnt/data/valetudo/config.json
If you're upgrading take a look at the release page for the new settings you need to add to that file
69be9df
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mqtt://user:password@host works with my secured MQTT server (iobroker).
69be9df
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great!
I've added a hint to the release notes
69be9df
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I can also confirm, it works without a problem (mosquitto) 👍