├── .github
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── question.yml
│ ├── feature-request.yml
│ └── bug-report.yml
├── dependabot.yml
├── stale.yml
└── workflows
│ └── node.js.yml
├── .gitignore
├── ecovacs-deebot.png
├── images
└── basic-flow-example.png
├── .npmignore
├── nodes
├── ecovacs-account.js
├── locales
│ ├── en
│ │ ├── ecovacs-account.json
│ │ ├── ecovacs-deebot.json
│ │ ├── ecovacs-account.html
│ │ ├── deebot-command.html
│ │ ├── ecovacs-deebot.html
│ │ └── deebot-command.json
│ └── de
│ │ ├── ecovacs-account.json
│ │ ├── ecovacs-deebot.json
│ │ ├── ecovacs-account.html
│ │ ├── deebot-command.html
│ │ ├── ecovacs-deebot.html
│ │ └── deebot-command.json
├── deebot-command.js
├── ecovacs-deebot.html
├── ecovacs-account.html
├── ecovacs-deebot.js
└── deebot-command.html
├── package.json
├── test
└── test.js
├── examples
├── Basic Flow.json
└── Only laser unit models - save map image to file.json
├── README.md
├── resources
└── commands.js
└── LICENSE
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_STORE
3 | .idea
4 | logs
5 | .vs/*
6 | .vscode/*
7 |
--------------------------------------------------------------------------------
/ecovacs-deebot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrbungle64/node-red-contrib-ecovacs-deebot/HEAD/ecovacs-deebot.png
--------------------------------------------------------------------------------
/images/basic-flow-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrbungle64/node-red-contrib-ecovacs-deebot/HEAD/images/basic-flow-example.png
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .git
2 | .idea
3 | node_modules/
4 | tsconfig.json
5 | .eslintrc.json
6 | .eslintrc.js
7 | .vs/*
8 | .vscode/*
9 |
10 | # npm package files
11 | package-lock.json
12 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "npm" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "daily"
12 |
--------------------------------------------------------------------------------
/nodes/ecovacs-account.js:
--------------------------------------------------------------------------------
1 | module.exports = function(RED) {
2 | function EcovacsAccountNode(n) {
3 | RED.nodes.createNode(this,n);
4 | this.name = n.name;
5 | this.email = this.credentials.email;
6 | this.password = this.credentials.password;
7 | this.countryCode = n.countryCode;
8 | this.login = n.login;
9 | }
10 | RED.nodes.registerType("ecovacs-account",EcovacsAccountNode,{
11 | credentials: {
12 | email: {type:"text"},
13 | password: {type:"password"}
14 | }
15 | });
16 | }
17 |
--------------------------------------------------------------------------------
/nodes/locales/en/ecovacs-account.json:
--------------------------------------------------------------------------------
1 | {
2 | "ecovacs-account" : {
3 | "countryCode": {
4 | "label": "Country code",
5 | "placeholder": "Insert your 2-digit country code here"
6 | },
7 | "email": {
8 | "label": "Email / Ecovacs ID",
9 | "placeholder": "Insert email address or ID of your Ecovacs account"
10 | },
11 | "name": {
12 | "label": "Name"
13 | },
14 | "password": {
15 | "label": "Password",
16 | "placeholder": "Insert password of your Ecovacs account"
17 | },
18 | "login": {
19 | "label": "Login"
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/nodes/locales/de/ecovacs-account.json:
--------------------------------------------------------------------------------
1 | {
2 | "ecovacs-account" : {
3 | "countryCode": {
4 | "label": "Länder-Code",
5 | "placeholder": "Bitte 2-stelligen Ländercode hier eingeben"
6 | },
7 | "email": {
8 | "label": "E-Mail-Adresse / Ecovacs ID",
9 | "placeholder": "E-Mail-Adresse oder ID Ihres Ecovacs Kontos"
10 | },
11 | "name": {
12 | "label": "Name"
13 | },
14 | "password": {
15 | "label": "Passwort",
16 | "placeholder": "Passwort Ihres Ecovacs Kontos hier eingeben"
17 | },
18 | "login": {
19 | "label": "Login"
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/nodes/locales/de/ecovacs-deebot.json:
--------------------------------------------------------------------------------
1 | {
2 | "ecovacs-deebot" : {
3 | "account": {
4 | "label": "Konto"
5 | },
6 | "connectOnStartup": {
7 | "label": "Beim Starten verbinden"
8 | },
9 | "enableGetMapsOnStartup": {
10 | "label": "Beim Starten Map Objekt abfragen (inkl. Karte bei 950 basierten Modellen)"
11 | },
12 | "deviceNumber": {
13 | "label": "Gerätenummer",
14 | "placeholder": "Gerätenummer eingeben"
15 | },
16 | "enableSimpleEvents": {
17 | "label": "Ausgabe einfache Events aktivieren (zusätzliche zu kombinierten Events)"
18 | },
19 | "name": {
20 | "label": "Name"
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/nodes/locales/en/ecovacs-deebot.json:
--------------------------------------------------------------------------------
1 | {
2 | "ecovacs-deebot" : {
3 | "account": {
4 | "label": "Account"
5 | },
6 | "connectOnStartup": {
7 | "label": "Connect on startup"
8 | },
9 | "enableGetMapsOnStartup": {
10 | "label": "Retrieve map data object at startup (incl. map image for 950 type models)"
11 | },
12 | "deviceNumber": {
13 | "label": "Device number",
14 | "placeholder": "Insert number of device here"
15 | },
16 | "enableSimpleEvents": {
17 | "label": "Enable output of simple events (additional to combined event output)"
18 | },
19 | "name": {
20 | "label": "Name"
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 5
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 3
5 | # Only issues or pull requests with all of these labels are check if stale
6 | onlyLabels:
7 | - "Waiting for feedback"
8 | # Issues with these labels will never be considered stale
9 | exemptLabels:
10 | - Pinned
11 | - Security
12 | # Label to use when marking an issue as stale
13 | staleLabel: Wontfix
14 | # Comment to post when marking an issue as stale. Set to `false` to disable
15 | markComment: >
16 | This issue has been automatically marked as stale because it has not had
17 | recent activity. It will be closed if no further activity occurs. Thank you
18 | for your contributions.
19 | # Comment to post when closing a stale issue. Set to `false` to disable
20 | closeComment: >
21 | This issue has been automatically closed.
22 |
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: [ master ]
11 |
12 | jobs:
13 | build:
14 | runs-on: ubuntu-latest
15 | strategy:
16 | fail-fast: false
17 | matrix:
18 | node-version: [20.x, 22.x]
19 |
20 | steps:
21 | - name: Checkout
22 | uses: actions/checkout@v4
23 |
24 | - name: Setup Node ${{ matrix.node-version }}
25 | uses: actions/setup-node@v4
26 | with:
27 | node-version: ${{ matrix.node-version }}
28 | cache: npm
29 | cache-dependency-path: |
30 | package-lock.json
31 |
32 | - name: Install dependencies
33 | run: npm ci
34 |
35 | - name: Build
36 | run: npm run build --if-present
37 |
38 | - name: Test
39 | run: npm test
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-red-contrib-ecovacs-deebot",
3 | "version": "0.4.13",
4 | "description": "Node-RED node for Ecovacs Deebot vacuum cleaner",
5 | "main": "nodes/ecovacs-deebot.js",
6 | "scripts": {
7 | "test": "NODE_ENV=test mocha"
8 | },
9 | "author": "Sascha Hölzel",
10 | "keywords": [
11 | "node-red",
12 | "ecovacs",
13 | "deebot",
14 | "ozmo",
15 | "yeedi",
16 | "vacuum",
17 | "robot",
18 | "airbot",
19 | "air purifier",
20 | "smart home",
21 | "home automation"
22 | ],
23 | "license": "GPL-3.0",
24 | "repository": {
25 | "type": "git",
26 | "url": "https://github.com/mrbungle64/node-red-contrib-ecovacs-deebot"
27 | },
28 | "node-red": {
29 | "version": ">=1.3.0",
30 | "nodes": {
31 | "ecovacs-account": "nodes/ecovacs-account.js",
32 | "ecovacs-deebot": "nodes/ecovacs-deebot.js",
33 | "ecovacs-deebot-command": "nodes/deebot-command.js"
34 | }
35 | },
36 | "dependencies": {
37 | "ecovacs-deebot": "0.9.6-beta.11",
38 | "node-machine-id": "1.1.12"
39 | },
40 | "devDependencies": {
41 | "mocha": "^10.8.2",
42 | "@types/node-red": "^1.3.5"
43 | },
44 | "engines": {
45 | "node": ">=20"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/nodes/locales/en/ecovacs-account.html:
--------------------------------------------------------------------------------
1 |
30 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert');
2 |
3 | const resourcesCommands = require('../resources/commands');
4 | const germanTranslation = require('../nodes/locales/de/deebot-command.json')["ecovacs-deebot-command"];
5 | const englishTranslation = require('../nodes/locales/en/deebot-command.json')["ecovacs-deebot-command"];
6 |
7 | describe('Commands', () => {
8 | for (const command in resourcesCommands.commands) {
9 | describe('For command ' + command, () => {
10 | it('should find a payload', function () {
11 | assert.ok(resourcesCommands.commands[command].hasOwnProperty('payload'));
12 | });
13 | it('should find a German translation', function () {
14 | assert.ok(germanTranslation.hasOwnProperty(command));
15 | });
16 | it('should find a English translation', function () {
17 | assert.ok(englishTranslation.hasOwnProperty(command));
18 | });
19 | if (resourcesCommands.commands[command].hasOwnProperty('info')) {
20 | const infoArray = resourcesCommands.commands[command]['info'];
21 | for (let i = 0; i < infoArray.length; i++) {
22 | const info = infoArray[i];
23 | it('should find a German translation for info ' + info, function () {
24 | assert.ok(germanTranslation.hasOwnProperty('info_' + info));
25 | });
26 | it('should find a English translation for info ' + info, function () {
27 | assert.ok(englishTranslation.hasOwnProperty('info_' + info));
28 | });
29 | }
30 | }
31 | });
32 | }
33 | });
34 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.yml:
--------------------------------------------------------------------------------
1 | name: Question
2 | description: Ask a question
3 | labels: [ Question ]
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: |
8 | This issue form is for questions only!
9 | If you have a problem or a feature request, please use one of the other templates.
10 | - type: textarea
11 | validations:
12 | required: true
13 | attributes:
14 | label: The question
15 | - type: markdown
16 | attributes:
17 | value: |
18 | ## Environment
19 | - type: dropdown
20 | id: device
21 | validations:
22 | required: false
23 | attributes:
24 | label: Which model do you own?
25 | options:
26 | - Deebot 500
27 | - Deebot 600/601/605
28 | - Deebot OZMO 610
29 | - Deebot 710/711/711s
30 | - Deebot 900/901
31 | - Deebot OZMO 900/905
32 | - Deebot OZMO 920
33 | - Deebot OZMO 930
34 | - Deebot OZMO 950
35 | - Deebot OZMO Slim 10/11
36 | - Deebot U2 series
37 | - Deebot N3 MAX
38 | - Deebot N7
39 | - Deebot N8 series
40 | - Deebot T8 series
41 | - Deebot T9 series
42 | - Deebot T10 series
43 | - Deebot X1 series
44 | - Deebot Slim 2
45 | - Deebot N79 series
46 | - Deebot M88
47 | - Other model
48 | - type: markdown
49 | attributes:
50 | value: |
51 | # Details
52 | - type: textarea
53 | attributes:
54 | label: Additional information
55 | description: >
56 | If you have any additional information for us, use the field below.
57 | Please note, you can attach screenshots or screen recordings here, by
58 | dragging and dropping files in the field below.
59 |
--------------------------------------------------------------------------------
/nodes/locales/de/ecovacs-account.html:
--------------------------------------------------------------------------------
1 |
33 |
--------------------------------------------------------------------------------
/nodes/deebot-command.js:
--------------------------------------------------------------------------------
1 | const {commands, getArgValue} = require("../resources/commands");
2 | module.exports = function (RED) {
3 |
4 | function EcovacsDeebotCommandNode(config) {
5 | RED.nodes.createNode(this, config);
6 | this.name = config.name;
7 | this.command = config.command;
8 | this.arg = config.arg;
9 | this.arg2 = config.arg2;
10 | this.arg3 = config.arg3;
11 |
12 | let node = this;
13 | node.config = config;
14 | node.on('input', (msg) => {
15 | sendCommand(node, msg);
16 | });
17 |
18 | function sendCommand(node, msg) {
19 | const output = {};
20 | let argValue;
21 |
22 | if (msg && commands.hasOwnProperty(node.config.command) ) {
23 | Object.assign(output, {payload: commands[node.config.command].payload});
24 | if (commands[node.config.command].hasOwnProperty("arg")) {
25 | argValue = getArgValue(node.config.command, node.config.arg, 1);
26 | Object.assign(output, {arg: argValue});
27 | if (commands[node.config.command].hasOwnProperty("arg2")) {
28 | argValue = getArgValue(node.config.command, node.config.arg2, 2);
29 | Object.assign(output, {arg2: argValue});
30 | if (commands[node.config.command].hasOwnProperty("arg3")) {
31 | argValue = getArgValue(node.config.command, node.config.arg3, 3);
32 | Object.assign(output, {arg3: argValue});
33 | }
34 | }
35 | }
36 | }
37 |
38 | if (output) {
39 | node.send(output);
40 | }
41 | }
42 | }
43 |
44 | RED.nodes.registerType("ecovacs-deebot-command", EcovacsDeebotCommandNode);
45 |
46 | if (RED.settings.version.substring(0, 3) < "1.3"){
47 | RED.httpAdmin.get('/deebot-command/*', RED.auth.needsPermission('deebot-command.read'), function(req, res) {
48 | const options = {
49 | root: __dirname + '/../resources/',
50 | dotfiles: 'deny'
51 | };
52 | res.sendFile(req.params[0], options);
53 | });
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request.yml:
--------------------------------------------------------------------------------
1 | name: Feature request
2 | description: Suggest an idea for this project
3 | labels: [ Enhancement, Verification ]
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: |
8 | This issue form is for feature requests only!
9 | If you have a problem or if you just want to ask a question, please use one of the other templates.
10 | - type: textarea
11 | validations:
12 | required: true
13 | attributes:
14 | label: The feature request
15 | description: >-
16 | Please provide a clear and concise description of what you want to happen.
17 | - type: markdown
18 | attributes:
19 | value: |
20 | ## Environment
21 | - type: dropdown
22 | id: device
23 | validations:
24 | required: false
25 | attributes:
26 | label: Which model do you own?
27 | options:
28 | - Deebot 600/601/605
29 | - Deebot OZMO 610
30 | - Deebot 710/711/711s
31 | - Deebot 900/901
32 | - Deebot OZMO 900/905
33 | - Deebot OZMO 920
34 | - Deebot OZMO 930
35 | - Deebot OZMO 950
36 | - Deebot OZMO Slim 10/11
37 | - Deebot U2 series
38 | - Deebot N3 MAX
39 | - Deebot N7
40 | - Deebot N8 series
41 | - Deebot T8 series
42 | - Deebot T9 series
43 | - Deebot T10 series
44 | - Deebot X1 series
45 | - Deebot Slim 2
46 | - Deebot N79 series
47 | - Deebot M88
48 | - Other model
49 | - type: input
50 | id: otherDevice
51 | validations:
52 | required: false
53 | attributes:
54 | label: Which "other" models are you using?
55 | placeholder: Deebot [OZMO] xxx
56 | description: >
57 | Only needed if the model is not listed above ("Which model do you own?") or if you use more than one model with this adapter
58 | - type: input
59 | id: version
60 | validations:
61 | required: true
62 | attributes:
63 | label: Which version of the Ecovacs Deebot node are you using?
64 | placeholder: e.g. 0.3.0
65 | - type: markdown
66 | attributes:
67 | value: |
68 | # Details
69 | - type: textarea
70 | attributes:
71 | label: Additional information
72 | description: >
73 | If you have any additional information for us, use the field below.
74 | Please note, you can attach screenshots or screen recordings here, by
75 | dragging and dropping files in the field below.
76 |
--------------------------------------------------------------------------------
/nodes/locales/en/deebot-command.html:
--------------------------------------------------------------------------------
1 |
51 |
--------------------------------------------------------------------------------
/nodes/locales/de/deebot-command.html:
--------------------------------------------------------------------------------
1 |
53 |
--------------------------------------------------------------------------------
/nodes/locales/en/ecovacs-deebot.html:
--------------------------------------------------------------------------------
1 |
60 |
--------------------------------------------------------------------------------
/nodes/locales/de/ecovacs-deebot.html:
--------------------------------------------------------------------------------
1 |
60 |
--------------------------------------------------------------------------------
/nodes/ecovacs-deebot.html:
--------------------------------------------------------------------------------
1 |
55 |
56 |
82 |
--------------------------------------------------------------------------------
/nodes/ecovacs-account.html:
--------------------------------------------------------------------------------
1 |
62 |
63 |
85 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.yml:
--------------------------------------------------------------------------------
1 | name: Problem or bug
2 | description: Create a report to help us improve
3 | labels: [ Potential bug, Verification ]
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: |
8 | This issue form is for reporting bugs or problems only!
9 | If you have a feature request or a question, please use one of the other templates.
10 | - type: checkboxes
11 | attributes:
12 | label: Is there an existing issue for this?
13 | description: Please search to see if an issue already exists for the bug you encountered.
14 | options:
15 | - label: I have searched the existing issues and no issue is describing my issue
16 | required: true
17 | - type: textarea
18 | validations:
19 | required: true
20 | attributes:
21 | label: The problem
22 | description: >-
23 | Describe the issue you are experiencing. Tell us what you were trying to do and what happened.
24 | Provide a clear and concise description of what the problem is.
25 | - type: markdown
26 | attributes:
27 | value: |
28 | ## Environment
29 | - type: dropdown
30 | id: device
31 | validations:
32 | required: true
33 | attributes:
34 | label: With which model does the problem occur?
35 | options:
36 | - Deebot 500
37 | - Deebot 600/601/605
38 | - Deebot OZMO 610
39 | - Deebot 710/711/711s
40 | - Deebot 900/901
41 | - Deebot OZMO 900/905
42 | - Deebot OZMO 920
43 | - Deebot OZMO 930
44 | - Deebot OZMO 950
45 | - Deebot OZMO Slim 10/11
46 | - Deebot U2 series
47 | - Deebot N3 MAX
48 | - Deebot N7
49 | - Deebot N8 series
50 | - Deebot T8 series
51 | - Deebot T9 series
52 | - Deebot T10 series
53 | - Deebot X1 series
54 | - Deebot Slim 2
55 | - Deebot N79 series
56 | - Deebot M88
57 | - Other model (please fill out the fields below)
58 | - type: input
59 | id: otherDevice
60 | validations:
61 | required: false
62 | attributes:
63 | label: Which "other" models are you using?
64 | placeholder: Deebot [OZMO] xxx
65 | description: >
66 | Only needed if the model is not listed above ("With which model does the problem occur?") or if you use more than one model with this adapter
67 | - type: input
68 | id: version
69 | validations:
70 | required: true
71 | attributes:
72 | label: Which version of the ecovacs-deebot node are you using?
73 | placeholder: e.g. 0.3.0
74 | - type: input
75 | id: node_red_version
76 | validations:
77 | required: true
78 | attributes:
79 | label: Which version of Node-RED you have installed?
80 | placeholder: e.g. 1.3.5, 2.0.5
81 | - type: input
82 | id: nodejs_version
83 | validations:
84 | required: true
85 | attributes:
86 | label: Which version of Node.js are you using?
87 | placeholder: e.g. v14.17.2
88 | - type: markdown
89 | attributes:
90 | value: |
91 | # Details
92 | - type: textarea
93 | attributes:
94 | label: Anything in the logs that might be useful?
95 | description: >
96 | For example, error message, or stack traces
97 | render: txt
98 | - type: textarea
99 | attributes:
100 | label: Additional information
101 | description: >
102 | If you have any additional information for us, use the field below.
103 | Please note, you can attach screenshots or screen recordings here, by
104 | dragging and dropping files in the field below.
105 |
--------------------------------------------------------------------------------
/examples/Basic Flow.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "ce32457c.36af08",
4 | "type": "ecovacs-deebot",
5 | "z": "f6f2187d.f17ca8",
6 | "account": "f58ee3da.650fc",
7 | "name": "Deebot Vacuum Cleaning Robot",
8 | "deviceNumber": "1",
9 | "connectOnStartup": false,
10 | "enableGetMapsOnStartup": false,
11 | "enableSimpleEvents": false,
12 | "x": 810,
13 | "y": 340,
14 | "wires": [
15 | [
16 | "429c4136.94907"
17 | ]
18 | ]
19 | },
20 | {
21 | "id": "b4cae92d.678908",
22 | "type": "inject",
23 | "z": "f6f2187d.f17ca8",
24 | "name": "",
25 | "props": [
26 | {
27 | "p": "payload"
28 | },
29 | {
30 | "p": "topic",
31 | "vt": "str"
32 | }
33 | ],
34 | "repeat": "",
35 | "crontab": "",
36 | "once": false,
37 | "onceDelay": 0.1,
38 | "topic": "",
39 | "payload": "true",
40 | "payloadType": "bool",
41 | "x": 250,
42 | "y": 300,
43 | "wires": [
44 | [
45 | "ac846fee.40856"
46 | ]
47 | ]
48 | },
49 | {
50 | "id": "6cbd5268.40e18c",
51 | "type": "inject",
52 | "z": "f6f2187d.f17ca8",
53 | "name": "",
54 | "props": [
55 | {
56 | "p": "payload"
57 | },
58 | {
59 | "p": "topic",
60 | "vt": "str"
61 | }
62 | ],
63 | "repeat": "",
64 | "crontab": "",
65 | "once": false,
66 | "onceDelay": 0.1,
67 | "topic": "",
68 | "payload": "true",
69 | "payloadType": "bool",
70 | "x": 250,
71 | "y": 380,
72 | "wires": [
73 | [
74 | "8f1a0d6.fbdd2f"
75 | ]
76 | ]
77 | },
78 | {
79 | "id": "429c4136.94907",
80 | "type": "debug",
81 | "z": "f6f2187d.f17ca8",
82 | "name": "",
83 | "active": true,
84 | "tosidebar": true,
85 | "console": false,
86 | "tostatus": false,
87 | "complete": "false",
88 | "statusVal": "",
89 | "statusType": "auto",
90 | "x": 1030,
91 | "y": 340,
92 | "wires": []
93 | },
94 | {
95 | "id": "ac846fee.40856",
96 | "type": "ecovacs-deebot-command",
97 | "z": "f6f2187d.f17ca8",
98 | "name": "",
99 | "command": "Connect",
100 | "arg": "",
101 | "arg2": "",
102 | "arg3": "",
103 | "x": 440,
104 | "y": 300,
105 | "wires": [
106 | [
107 | "ce32457c.36af08"
108 | ]
109 | ]
110 | },
111 | {
112 | "id": "8f1a0d6.fbdd2f",
113 | "type": "ecovacs-deebot-command",
114 | "z": "f6f2187d.f17ca8",
115 | "name": "",
116 | "command": "Disconnect",
117 | "arg": "",
118 | "arg2": "",
119 | "arg3": "",
120 | "x": 430,
121 | "y": 380,
122 | "wires": [
123 | [
124 | "ce32457c.36af08"
125 | ]
126 | ]
127 | },
128 | {
129 | "id": "731a0a8b.fd73a4",
130 | "type": "inject",
131 | "z": "f6f2187d.f17ca8",
132 | "name": "",
133 | "props": [
134 | {
135 | "p": "payload"
136 | },
137 | {
138 | "p": "topic",
139 | "vt": "str"
140 | }
141 | ],
142 | "repeat": "",
143 | "crontab": "",
144 | "once": false,
145 | "onceDelay": 0.1,
146 | "topic": "",
147 | "payload": "true",
148 | "payloadType": "bool",
149 | "x": 250,
150 | "y": 340,
151 | "wires": [
152 | [
153 | "5b444c21.79be44"
154 | ]
155 | ]
156 | },
157 | {
158 | "id": "5b444c21.79be44",
159 | "type": "ecovacs-deebot-command",
160 | "z": "f6f2187d.f17ca8",
161 | "name": "",
162 | "command": "Clean",
163 | "arg": "",
164 | "arg2": "",
165 | "arg3": "",
166 | "x": 470,
167 | "y": 340,
168 | "wires": [
169 | [
170 | "ce32457c.36af08"
171 | ]
172 | ]
173 | },
174 | {
175 | "id": "4b7b3fdf.9a1c5",
176 | "type": "comment",
177 | "z": "f6f2187d.f17ca8",
178 | "name": "Basic flow for node-red-contrib-ecovacs-deebot",
179 | "info": "Any trigger to the Deebot command node will prepare the correct payload to be executed in Ecovacs Deebot node.\n\nThe events from the robot will be send to debug node.",
180 | "x": 370,
181 | "y": 260,
182 | "wires": []
183 | },
184 | {
185 | "id": "f58ee3da.650fc",
186 | "type": "ecovacs-account",
187 | "countryCode": "DE",
188 | "name": "",
189 | "login": "ecovacs.com"
190 | }
191 | ]
192 |
--------------------------------------------------------------------------------
/examples/Only laser unit models - save map image to file.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "ce32457c.36af08",
4 | "type": "ecovacs-deebot",
5 | "z": "f6f2187d.f17ca8",
6 | "account": "f58ee3da.650fc",
7 | "name": "Deebot Vacuum Cleaning Robot",
8 | "deviceNumber": "1",
9 | "connectOnStartup": false,
10 | "enableGetMapsOnStartup": true,
11 | "enableSimpleEvents": false,
12 | "x": 770,
13 | "y": 380,
14 | "wires": [
15 | [
16 | "fb7c3142.574da"
17 | ]
18 | ]
19 | },
20 | {
21 | "id": "b4cae92d.678908",
22 | "type": "inject",
23 | "z": "f6f2187d.f17ca8",
24 | "name": "",
25 | "props": [
26 | {
27 | "p": "payload"
28 | },
29 | {
30 | "p": "topic",
31 | "vt": "str"
32 | }
33 | ],
34 | "repeat": "",
35 | "crontab": "",
36 | "once": false,
37 | "onceDelay": 0.1,
38 | "topic": "",
39 | "payload": "true",
40 | "payloadType": "bool",
41 | "x": 210,
42 | "y": 340,
43 | "wires": [
44 | [
45 | "ac846fee.40856"
46 | ]
47 | ]
48 | },
49 | {
50 | "id": "6cbd5268.40e18c",
51 | "type": "inject",
52 | "z": "f6f2187d.f17ca8",
53 | "name": "",
54 | "props": [
55 | {
56 | "p": "payload"
57 | },
58 | {
59 | "p": "topic",
60 | "vt": "str"
61 | }
62 | ],
63 | "repeat": "",
64 | "crontab": "",
65 | "once": false,
66 | "onceDelay": 0.1,
67 | "topic": "",
68 | "payload": "true",
69 | "payloadType": "bool",
70 | "x": 210,
71 | "y": 420,
72 | "wires": [
73 | [
74 | "8f1a0d6.fbdd2f"
75 | ]
76 | ]
77 | },
78 | {
79 | "id": "429c4136.94907",
80 | "type": "debug",
81 | "z": "f6f2187d.f17ca8",
82 | "name": "",
83 | "active": true,
84 | "tosidebar": true,
85 | "console": false,
86 | "tostatus": false,
87 | "complete": "false",
88 | "statusVal": "",
89 | "statusType": "auto",
90 | "x": 1170,
91 | "y": 400,
92 | "wires": []
93 | },
94 | {
95 | "id": "ac846fee.40856",
96 | "type": "ecovacs-deebot-command",
97 | "z": "f6f2187d.f17ca8",
98 | "name": "",
99 | "command": "Connect",
100 | "arg": "",
101 | "arg2": "",
102 | "arg3": "",
103 | "x": 400,
104 | "y": 340,
105 | "wires": [
106 | [
107 | "ce32457c.36af08"
108 | ]
109 | ]
110 | },
111 | {
112 | "id": "8f1a0d6.fbdd2f",
113 | "type": "ecovacs-deebot-command",
114 | "z": "f6f2187d.f17ca8",
115 | "name": "",
116 | "command": "Disconnect",
117 | "arg": "",
118 | "arg2": "",
119 | "arg3": "",
120 | "x": 390,
121 | "y": 420,
122 | "wires": [
123 | [
124 | "ce32457c.36af08"
125 | ]
126 | ]
127 | },
128 | {
129 | "id": "731a0a8b.fd73a4",
130 | "type": "inject",
131 | "z": "f6f2187d.f17ca8",
132 | "name": "",
133 | "props": [
134 | {
135 | "p": "payload"
136 | },
137 | {
138 | "p": "topic",
139 | "vt": "str"
140 | }
141 | ],
142 | "repeat": "",
143 | "crontab": "",
144 | "once": false,
145 | "onceDelay": 0.1,
146 | "topic": "",
147 | "payload": "true",
148 | "payloadType": "bool",
149 | "x": 210,
150 | "y": 380,
151 | "wires": [
152 | [
153 | "5b444c21.79be44"
154 | ]
155 | ]
156 | },
157 | {
158 | "id": "5b444c21.79be44",
159 | "type": "ecovacs-deebot-command",
160 | "z": "f6f2187d.f17ca8",
161 | "name": "",
162 | "command": "GetMapImage",
163 | "arg": "012345678",
164 | "arg2": "outline",
165 | "arg3": "",
166 | "x": 410,
167 | "y": 380,
168 | "wires": [
169 | [
170 | "ce32457c.36af08"
171 | ]
172 | ]
173 | },
174 | {
175 | "id": "4b7b3fdf.9a1c5",
176 | "type": "comment",
177 | "z": "f6f2187d.f17ca8",
178 | "name": "Flow for saving map image to file",
179 | "info": "When connected the robot should provide the map image as base64 data. These can be saved to a file and then be displayed e.g. on the dashboard or send somewhere else.\n\nIf you want to refresh the image data trigger the node which provides `Retrieve map image data`.\n\nThis currently works with 950 type models only (e.g. OZMO 920/950, T8/T9 series)",
180 | "x": 280,
181 | "y": 300,
182 | "wires": []
183 | },
184 | {
185 | "id": "fb7c3142.574da",
186 | "type": "switch",
187 | "z": "f6f2187d.f17ca8",
188 | "name": "Event-Type",
189 | "property": "payload.type",
190 | "propertyType": "msg",
191 | "rules": [
192 | {
193 | "t": "eq",
194 | "v": "MapImage",
195 | "vt": "str"
196 | },
197 | {
198 | "t": "else"
199 | }
200 | ],
201 | "checkall": "false",
202 | "repair": false,
203 | "outputs": 2,
204 | "x": 990,
205 | "y": 380,
206 | "wires": [
207 | [
208 | "429c4136.94907",
209 | "8baf9da3.4b091"
210 | ],
211 | [
212 | "429c4136.94907"
213 | ]
214 | ]
215 | },
216 | {
217 | "id": "8baf9da3.4b091",
218 | "type": "function",
219 | "z": "f6f2187d.f17ca8",
220 | "name": "Get raw data",
221 | "func": "return {\n payload: msg.payload.value.mapBase64PNG.split(';base64,').pop()\n};",
222 | "outputs": 1,
223 | "noerr": 0,
224 | "initialize": "",
225 | "finalize": "",
226 | "libs": [],
227 | "x": 1170,
228 | "y": 340,
229 | "wires": [
230 | [
231 | "d7f80e4b.b0032"
232 | ]
233 | ]
234 | },
235 | {
236 | "id": "d7f80e4b.b0032",
237 | "type": "file",
238 | "z": "f6f2187d.f17ca8",
239 | "name": "",
240 | "filename": "mapImage.jpg",
241 | "appendNewline": true,
242 | "createDir": false,
243 | "overwriteFile": "false",
244 | "encoding": "none",
245 | "x": 1340,
246 | "y": 340,
247 | "wires": [
248 | []
249 | ]
250 | },
251 | {
252 | "id": "f58ee3da.650fc",
253 | "type": "ecovacs-account",
254 | "countryCode": "DE",
255 | "name": "",
256 | "login": "ecovacs.com"
257 | }
258 | ]
259 |
--------------------------------------------------------------------------------
/nodes/locales/en/deebot-command.json:
--------------------------------------------------------------------------------
1 | {
2 | "ecovacs-deebot-command" : {
3 | "AddVirtualBoundary": "+ Add virtual boundary",
4 | "AirDryingStart": "► Start air-drying",
5 | "AirDryingStop": "◼ Stop air-drying",
6 | "Charge": "⏮ Return to charging station",
7 | "Clean": "► Start automatic cleaning",
8 | "Clean_V2": "► Start automatic cleaning (V2)",
9 | "Connect": "⏻ Connect",
10 | "CustomArea": "► Start custom area cleaning",
11 | "DeleteVirtualBoundary": "– Delete virtual boundary",
12 | "description_Connect": "Connect to robot",
13 | "description_Disconnect": "Disconnect from robot",
14 | "description_FindMe": "Play \"I am here\"",
15 | "description_GetMapImage": "Get map image as base64 data-URI (PNG format)",
16 | "description_GetWaterBoxInfo": "Query whether the water box is installed",
17 | "description_GetWaterLevel": "Retrieve the set water flow level for mopping",
18 | "Disconnect": "⭘ Disconnect",
19 | "EnableSweepMode": "⚙ Enable \"mopping only\"",
20 | "DisableSweepMode": "⚙ Disable \"mopping only\"",
21 | "EnableBorderSpin": "⚙ Enable \"corner deep cleaning\"",
22 | "DisableBorderSpin": "⚙ Disable \"corner deep cleaning\"",
23 | "EnableVoiceAssistant": "⚙ Enable YIKO voice assistant",
24 | "DisableVoiceAssistant": "⚙ Disable YIKO voice assistant",
25 | "EnableAutoEmpty": "⚙ Enable Auto-Empty",
26 | "DisableAutoEmpty": "⚙ Disable Auto-Empty",
27 | "EnableCarpetPressure": "⚙ Enable Auto-Boost Suction",
28 | "DisableCarpetPressure": "⚙ Disable Auto-Boost Suction",
29 | "EnableCleanPreference": "⚙ Enable Clean Preference mode",
30 | "DisableCleanPreference": "⚙ Disable Clean Preference mode",
31 | "EnableDoNotDisturb": "⚙ Enable Do Not Disturb mode",
32 | "DisableDoNotDisturb": "⚙ Disable Do Not Disturb mode",
33 | "EnableAdvancedMode": "⚙ Enable Advanced Mode",
34 | "DisableAdvancedMode": "⚙ Disable Advanced Mode",
35 | "EnableTrueDetect": "⚙ Enable True Detect 3D",
36 | "DisableTrueDetect": "⚙ Disable True Detect 3D",
37 | "dummy_air_purifier": "--- AIRBOT (air purifier) ---",
38 | "dummy_auto_empty_station": "--- Auto empty station ---",
39 | "dummy_cleaning": "--- Cleaning ---",
40 | "dummy_cleaning_log": "--- Cleaning log and consumable ---",
41 | "dummy_cleaning_station": "--- Cleaning station ---",
42 | "dummy_connection_handling": "--- Connection handling ---",
43 | "dummy_manual_control": "--- Manual control ---",
44 | "dummy_map_data": "--- Map data, spot areas and virtual boundaries ---",
45 | "dummy_mopping": "--- Mopping and water box ---",
46 | "dummy_position": "--- Position ---",
47 | "dummy_retrieve_states": "--- Retrieve states ---",
48 | "dummy_spot_areas": "--- Spot areas ---",
49 | "dummy_vacuum_power": "--- Vacuum power ---",
50 | "dummy_various_control_commands": "--- Various control commands ---",
51 | "dummy_various_other_commands": "--- Various other commands ---",
52 | "dummy_virtual_boundaries": "--- Virtual boundaries ---",
53 | "dummy_voice_and_sound": "--- Voice and sound ---",
54 | "Edge": "► Start edge cleaning",
55 | "FindMe": "◎ Find me",
56 | "GetAdvancedMode": "↶ Retrieve Advanced Mode status",
57 | "GetAirQuality": "↶ Retrieve values for air quality (e.g. PM2.5, PM10)",
58 | "GetAutoEmpty": "↶ Retrieve auto empty status",
59 | "GetBatteryState": "↶ Retrieve battery state",
60 | "GetChargerPos": "↶ Retrieve charger position",
61 | "GetChargeState": "↶ Retrieve charge state",
62 | "GetCleanLogs": "↶ Retrieve clean logs",
63 | "GetCleanSpeed": "↶ Retrieve vacuum power level",
64 | "GetCleanState": "↶ Retrieve clean state",
65 | "GetCleanState_V2": "↶ Retrieve clean state (V2)",
66 | "GetCleanSum": "↶ Retrieve clean summary",
67 | "GetLifeSpan": "↶ Retrieve accessories usage data",
68 | "GetMapImage": "↶ Retrieve map image data",
69 | "GetMaps": "↶ Retrieve full map data object",
70 | "GetMapsBasic": "↶ Retrieve basic map data",
71 | "GetNetInfo": "↶ Retrieve networking infos",
72 | "GetOnOff_continuous_cleaning": "↶ Retrieve continuous cleaning mode info (deprecated)",
73 | "GetOnOff_do_not_disturb": "↶ Retrieve do-not-disturb mode info (deprecated)",
74 | "GetOnOff_silence_voice_report": "↶ Retrieve silence voice report info",
75 | "GetContinuousCleaning": "↶ Retrieve continuous cleaning mode info",
76 | "GetDoNotDisturb": "↶ Retrieve do-not-disturb mode info",
77 | "GetPosition": "↶ Retrieve position of the robot",
78 | "GetSchedule": "↶ Retrieve cleaning schedule",
79 | "GetSleepStatus": "↶ Retrieve sleeping mode status",
80 | "GetSpotAreaInfo": "↶ Retrieve spot area infos",
81 | "GetSpotAreas": "↶ Retrieve spot areas",
82 | "GetTrueDetect": "↶ Retrieve TrueDetect status",
83 | "GetVirtualBoundaries": "↶ Retrieve virtual boundaries",
84 | "GetVirtualBoundaryInfo": "↶ Retrieve virtual boundary infos",
85 | "GetVolume": "↶ Retrieve volume",
86 | "GetWaterBoxInfo": "↶ Retrieve water box info",
87 | "GetWaterLevel": "↶ Retrieve water flow level",
88 | "info_950Type": "950 type models only (e.g. OZMO 920/950, N8/T8/T9/T10/X1 series)",
89 | "info_950Type_V2": "Current models only (e.g. N8/T8/T9/T10/X1 series)",
90 | "info_X1Series": "Only X1 series or similar models",
91 | "info_airPurifier": "Works also with air purifiers (e.g. AIRBOT Z1)",
92 | "info_airPurifier_only": "Air purifiers only (e.g. AIRBOT Z1)",
93 | "info_canvasLibrary": "Requires the installation of the canvas library",
94 | "info_cleaningStation": "Models with cleaning station only",
95 | "info_enableSimpleEvents": "Enable output of simple events must be enabled",
96 | "info_experimental": "Experimental",
97 | "info_laserType": "Models with laser unit only",
98 | "info_mainBrush": "Models with main brush only",
99 | "info_moppingSystem": "Models with mopping system only",
100 | "info_non950Type": "Some older models only (e.g. OZMO 930, Deebot 900/901)",
101 | "info_nonLaserType": "Models without laser unit only",
102 | "info_notWorking": "Not supported by most models",
103 | "info_ozmo930": "Works only on OZMO 930",
104 | "info_suctionStation": "Models with suction station only",
105 | "info_trueDetect": "Models with TrueDetect 3D only",
106 | "info_worksOnlyOnce": "Works only once in sequence (and on some models not at all)",
107 | "label_areas": "IDs of the spot areas (e.g. 0,5)",
108 | "label_boundaryCoordinates": "Boundary coordinates (e.g. -1320,-1100,1705,1200)",
109 | "label_boundaryID": "ID of the boundary (e.g. 5)",
110 | "label_boundaryType": "Type of the virtual boundary
- \"vw\": virtual wall
- \"mw\": no-mop-zone
",
111 | "label_command": "Command",
112 | "label_includeMapImage": "Include map image",
113 | "label_level": "Level",
114 | "label_mapID": "ID of the map (e.g. 1286981979)",
115 | "label_mapInfoType": "Map type
- \"outline\": outline
- \"wifiHeatMap\": WiFi coverage map
",
116 | "label_mapSetID": "ID of the map set (msid)",
117 | "label_name": "Name",
118 | "label_numberOfCleanings": "Number of cleanings",
119 | "label_soundID": "ID of the sound (for a list of sound IDs see here)",
120 | "label_spotAreaID": "Spot area ID (e.g. 5)",
121 | "label_timeStart": "Start (time)",
122 | "label_timeEnd": "End (time)",
123 | "label_value": "Value",
124 | "label_volume": "Volume",
125 | "MoveBackward": "◄ Move backward",
126 | "MoveForward": "► Move forward",
127 | "MoveLeft": "▲ Move left",
128 | "MoveRight": "▼ Move right",
129 | "Pause": "⏸ Pause the cleaning",
130 | "PlaySound_custom": "♫ Play custom sound",
131 | "PlaySound": "♫ Play sound",
132 | "Relocate": "◎ Relocate",
133 | "RenameSpotArea": "⌶ Rename spot area",
134 | "ResetLifeSpan_filter": "↺ Reset filter",
135 | "ResetLifeSpan_main_brush": "↺ Reset main brush",
136 | "ResetLifeSpan_side_brush": "↺ Reset side brush",
137 | "ResetLifeSpan_unit_care": "↺ Reset other components",
138 | "Resume": "► Resume the cleaning",
139 | "SetAutoEmpty": "⚙ Set auto empty",
140 | "SetCleanSpeed": "⚙ Set vacuum power level",
141 | "SetOnOff_continuous_cleaning": "⚙ Set continuous cleaning mode",
142 | "SetOnOff_do_not_disturb": "⚙ Set do-not-disturb mode (legacy models)",
143 | "SetOnOff_silence_voice_report": "⚙ Set silence voice report",
144 | "SetVolume": "⚙ Set volume",
145 | "SetWaterLevel": "⚙ Set water level",
146 | "Spot": "► Start spot cleaning",
147 | "SpotArea": "► Start spot area cleaning",
148 | "Stop": "◼ Stop cleaning",
149 | "WashingStart": "► Start self-cleaning",
150 | "WashingStop": "◼ Stop self-cleaning"
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Ecovacs Deebot node for Node-RED
4 | [](https://www.npmjs.com/package/node-red-contrib-ecovacs-deebot)
5 | [](https://www.npmjs.com/package/node-red-contrib-ecovacs-deebot)
6 | [](https://www.npmjs.com/package/node-red-contrib-ecovacs-deebot)
7 | [](https://github.com/mrbungle64/node-red-contrib-ecovacs-deebot)
8 |
9 | Node-RED node for running Ecovacs Deebot vacuum cleaner robots.
10 |
11 | There's also initial support for some yeedi models (experimental).
12 |
13 | This node uses the [ecovacs-deebot.js](https://github.com/mrbungle64/ecovacs-deebot.js) library.
14 |
15 | ## Features
16 |
17 | Some noteworthy features are:
18 |
19 | * Basic cleaning functions (e.g. auto clean, spot area, custom area etc.)
20 | * and various other commands (e.g. play sound, reset consumables, relocate position etc.)
21 | * Retrieve basic information (e.g. battery level, cleaning log, consumable, cleaning and charging status etc.)
22 | * and various extended information (e.g. charging position, current map, network information)
23 | * Retrieve information during the cleaning process (e.g. current position and current spot area)
24 | * Set basic and advanced settings (e.g. continuous cleaning, do-not-disturb mode, TrueDetect 3D, volume etc.)
25 | * Adjustment of vacuum power and water level
26 | * Function for loading the current map image
27 |
28 | ## Usage
29 |
30 | ### Ecovacs account
31 |
32 | First you have to configure the Ecovacs (or yeedi) account:
33 | - "Name" (optional)
34 | - "Login" (Ecovacs or yeedi)
35 | - "Email / Ecovacs ID" (email or account ID)
36 | - "Password"
37 | - "Country code" (see [here](https://github.com/mrbungle64/node-red-contrib-ecovacs-deebot/wiki/Country-codes) for a list of country codes)
38 |
39 | ### Available nodes
40 |
41 | #### Ecovacs Deebot
42 |
43 | Available options:
44 | - "Account"
45 | - "Name" of the Device
46 | - "Device number"
47 | - "Connect on startup"
48 | - "Retrieve map data object at startup" (experimental)
49 | - "Enable output of simple events" (experimental)
50 |
51 | #### Deebot command
52 |
53 | Available options:
54 | - "Command" (e.g. `Start automatic cleaning`, `Retrieve battery state`)
55 | - Command specific fields (e.g. `Map ID`, `Spot area ID`)
56 |
57 | ### Examples
58 |
59 | There are 2 example flows provided by this node:
60 |
61 | * Basic Flow
62 | * Only laser unit models - save map image to file
63 |
64 | You can directly import them from the editor using the [Import dialog](https://nodered.org/docs/user-guide/editor/workspace/import-export).
65 |
66 | 
67 |
68 | ## Models
69 |
70 | ### Supported models
71 |
72 | * Deebot OZMO 920/950
73 | * Deebot OZMO T8 AIVI
74 | * Deebot X1 Turbo
75 |
76 | ### Other models
77 |
78 | The following models should work properly or at least partially.
79 | They are either already known to work or are technically similar to these models.
80 | Nevertheless, the functionality may be partially limited.
81 |
82 | I try to achieve a wide range of functionality, but decide this case by case depending on complexity and various other criteria.
83 | There is of course no claim to full functionality.
84 |
85 | #### Ecovacs Deebot
86 |
87 | * Deebot N8 series
88 | * Deebot T8 series
89 | * Deebot T9 series
90 | * Deebot T10 series
91 | * Deebot T20 series
92 | * Deebot X1 series
93 | * Deebot X2 series
94 |
95 | #### Airbot
96 |
97 | * Airbot Z1
98 |
99 | #### yeedi
100 |
101 | * yeedi k650
102 | * yeedi 2 hybrid
103 | * yeedi vac hybrid
104 | * yeedi vac max
105 | * yeedi vac 2 pro
106 | * yeedi mop station
107 |
108 | **Note**: The lists may not be fully complete
109 |
110 | ### Legacy models (soon to be discontinued)
111 |
112 | Legacy models that use XML for data transport (e.g. Deebot OZMO 930, Deebot 900/901) are mostly still working,
113 | but support for these models will be discontinued sooner or later.
114 |
115 | Please check [this readme](https://github.com/mrbungle64/ecovacs-deebot.js#legacy-models-soon-to-be-discontinued) for more information.
116 |
117 | ## Installation
118 |
119 | The minimum required version is 18.x
120 |
121 | This node uses the [node-canvas](https://www.npmjs.com/package/canvas) library for some map-related functionality which may require the installation of some additional packages.
122 |
123 | The installation of canvas is optional and not necessary for models without map functionality, but for full functional range please install the following packages.
124 |
125 | For Debian-based Linux systems the following commands should be executed:
126 |
127 | ```bash
128 | sudo apt-get update
129 | sudo apt-get install build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev
130 | ```
131 |
132 | Installation for canvas on alpine based systems (e.g. Node-RED docker container):
133 | ```bash
134 | apk add --no-cache build-base g++ cairo-dev jpeg-dev pango-dev giflib-dev
135 | ```
136 |
137 | A reboot might be necessary before executing the next command
138 | ```bash
139 | npm install canvas --unsafe-perm=true
140 | ```
141 |
142 | For instructions for other systems visit https://www.npmjs.com/package/canvas#compiling
143 |
144 | ## Known issues
145 |
146 | * The "move" function varies from model to model, so it's not implemented universally
147 | * The generation of map images is not stable on 32-bit systems
148 | * and it still does not work properly with the Deebot X1 series and other current models
149 |
150 | ## Changelog
151 |
152 | ### 0.4.13
153 | * Bumped minimum required version of Node.js to 18.x
154 | * Bumped ecovacs-deebot.js to latest beta version
155 |
156 | ### 0.4.12
157 | * Bumped ecovacs-deebot.js to latest beta version
158 |
159 | ### 0.4.11
160 | * Bumped ecovacs-deebot.js to latest alpha version
161 |
162 | ### 0.4.10
163 | * Bumped ecovacs-deebot.js to latest alpha version
164 | * A few changes for AIRBOT Z1 and Z1 Air Quality Monitor
165 |
166 | ### 0.4.9
167 | * Using library version 0.9.6 (alpha)
168 |
169 | ### 0.4.8
170 | * Using library version 0.9.5
171 |
172 | ### 0.4.7
173 | * Using library version 0.9.4
174 | * Add some commands for the cleaning station (X1 Omni/Turbo and similar models)
175 |
176 | ### 0.4.6
177 | * Using library version 0.9.2 (beta)
178 | * Fixed cleaning log for N8 series
179 |
180 | ### 0.4.5
181 | * Added some commands for AIRBOT Z1
182 |
183 | ### 0.4.4
184 | * Using library version 0.9.1 (alpha)
185 | * Fixed cleaning log for T9 based models
186 |
187 | ### 0.4.3
188 | * Using library version 0.9.0 (beta)
189 | * Some minor improvements and fixes
190 |
191 | ### 0.4.2
192 | * Using library version 0.8.3 (alpha)
193 | * Added initial support for yeedi login (experimental)
194 |
195 | ### 0.4.1
196 | * Using library version 0.8.2
197 | * Some minor corrections
198 |
199 | ### 0.4.0
200 | * Using library version 0.8.1
201 | * Added symbols to the command select box
202 | * Added some events (e.g. DusterRemind, WaterBoxMoppingType, WaterBoxScrubbingType, CleanCount)
203 | * Some further improvements and fixes
204 |
205 | ### 0.3.17
206 | * Using the latest library version (beta)
207 | * Added commands for Auto-Boost Suction and Clean Preference
208 | * Corrected some labels for commands in the German version
209 | * Some further improvements and fixes
210 |
211 | ### 0.3.16
212 | * Using library version 0.8.1 (beta)
213 | * Some improvements and fixes
214 |
215 | ### 0.3.14 - 0.3.15
216 | * Using library version 0.8.0 (beta)
217 |
218 | ### 0.3.13
219 | * Updated dependencies
220 |
221 | ### 0.3.9 - 0.3.12
222 | * Using library version 0.7.2 (alpha)
223 | * Stability improvements
224 | * Some minor changes and corrections
225 |
226 | ### 0.3.8
227 | * Bumped some dependencies (incl. fix for CVE-2022-0155)
228 |
229 | ### 0.3.7
230 | * Fix the cleaning functions for the Deebot 710 series
231 |
232 | ### 0.3.6
233 | * Using library version 0.7.0
234 | * Some smaller improvements
235 |
236 | ### 0.3.5
237 | * Using library version 0.6.8
238 |
239 | ### 0.3.4
240 | * Using library version 0.6.7
241 |
242 | ### 0.3.3
243 | * Using library version 0.6.6
244 |
245 | ### 0.3.2
246 | * Using library version 0.6.3
247 | * Bump minimum required version of Node.js to 12.x
248 |
249 | ### 0.3.1
250 | * Using library version 0.6.1-beta.7
251 | * Add option for GetMaps command whether to include map image
252 | * Some fixes and improvements
253 |
254 | ### 0.3.0
255 | * Using library version 0.6.1-beta.3
256 | * Add option to enable retrieving map object on startup
257 | * Add GetSchedule command
258 |
259 | ### 0.2.9
260 | * Using library version 0.6.1-beta.2
261 | * Add AdvanceMode command (950 type models)
262 | * Add TrueDetect command (experimental)
263 |
264 | ### 0.2.8
265 | * Using library version 0.6.1-alpha.15
266 | * (unclej84) Add example for saving map image to jpg file
267 |
268 | ### 0.2.7
269 | * (unclej84) Improve loading of commands.js for deebot-command node
270 | * (unclej84) Add basic example
271 |
272 | ### 0.2.6
273 | * (unclej84) Make node compatible with pre-1.3-versions
274 |
275 | ### 0.2.5
276 | * Using library version 0.6.1-alpha.11
277 | * Some minor changes and fixes
278 |
279 | ### 0.2.4
280 | * Using library version 0.6.1-alpha.9
281 | * Add EnableDoNotDisturb and DisableDoNotDisturb commands (950 type models)
282 |
283 | ### 0.2.3
284 | * (unclej84) Help texts for display in editor sidebar
285 |
286 | ### 0.2.2
287 | * Some minor fixes and improvements
288 |
289 | ### 0.2.1
290 | * Device number starts with 1
291 |
292 | ### 0.2.0
293 | * Initial npm release
294 | * Add a lot of commands and events
295 | * Using library version 0.6.1-alpha.6
296 | * (unclej84) Add Multi-language support
297 | * (unclej84) A lot of improvements for the node editor
298 | * (unclej84) Use credentials for account data
299 |
300 | ## Disclaimer
301 |
302 | I am in no way affiliated with Ecovacs Robotics Co., Ltd. or yeedi Technology Limited.
303 |
304 | ## License
305 |
306 | GNU GENERAL PUBLIC LICENSE
307 |
308 | Copyright (c) 2023 Sascha Hölzel
309 |
--------------------------------------------------------------------------------
/nodes/locales/de/deebot-command.json:
--------------------------------------------------------------------------------
1 | {
2 | "ecovacs-deebot-command" : {
3 | "AddVirtualBoundary": "+ Virtuelle Begrenzung erzeugen",
4 | "AirDryingStart": "► Lufttrocknung starten",
5 | "AirDryingStop": "◼ Lufttrocknung stoppen",
6 | "Charge": "⏮ Zurück zur Ladestation",
7 | "Clean": "► Automatische Reinigung starten",
8 | "Clean_V2": "► Automatische Reinigung starten (V2)",
9 | "Connect": "⏻ Verbindung herstellen",
10 | "CustomArea": "► Benutzerdefinierten Bereich reinigen",
11 | "DeleteVirtualBoundary": "– Virtuelle Begrenzung löschen",
12 | "description_Connect": "Verbindung zum Roboter herstellen",
13 | "description_Disconnect": "Verbindung zum Roboter trennen",
14 | "description_FindMe": "\"Ich bin hier\" abspielen",
15 | "description_GetMapImage": "Bild der Karte abfragen (PNG-Format)",
16 | "description_GetWaterBoxInfo": "Abfragen, ob der Wassertank eingesetzt ist",
17 | "description_GetWaterLevel": "Eingestellte Wasserdurchflussrate abfragen",
18 | "Disconnect": "⭘ Verbindung trennen",
19 | "EnableSweepMode": "⚙ \"Nur wischen\" einschalten",
20 | "DisableSweepMode": "⚙ \"Nur wischen\" ausschalten",
21 | "EnableBorderSpin": "⚙ \"Ecken-Tiefenreinigung\" einschalten",
22 | "DisableBorderSpin": "⚙ \"Ecken-Tiefenreinigung\" ausschalten",
23 | "EnableVoiceAssistant": "⚙ YIKO voice assistant einschalten",
24 | "DisableVoiceAssistant": "⚙ YIKO voice assistant ausschalten",
25 | "EnableAutoEmpty": "⚙ Auto-Empty einschalten",
26 | "DisableAutoEmpty": "⚙ Auto-Empty ausschalten",
27 | "EnableCarpetPressure": "⚙ Auto-Saugkraftverstärkung einschalten",
28 | "DisableCarpetPressure": "⚙ Auto-Saugkraftverstärkung ausschalten",
29 | "EnableCleanPreference": "⚙ Reinigungseinstellungen (Modus) einschalten",
30 | "DisableCleanPreference": "⚙ Reinigungseinstellungen (Modus) ausschalten",
31 | "EnableDoNotDisturb": "⚙ Nicht-Stören-Modus einschalten",
32 | "DisableDoNotDisturb": "⚙ Nicht-Stören-Modus ausschalten",
33 | "EnableAdvancedMode": "⚙ Erweiterten Modus einschalten",
34 | "DisableAdvancedMode": "⚙ Erweiterten Modus ausschalten",
35 | "EnableTrueDetect": "⚙ True Detect 3D einschalten",
36 | "DisableTrueDetect": "⚙ True Detect 3D ausschalten",
37 | "dummy_air_purifier": "--- AIRBOT (air purifier) ---",
38 | "dummy_auto_empty_station": "--- Absaugstation ---",
39 | "dummy_cleaning": "--- Reinigung ---",
40 | "dummy_cleaning_log": "--- Reinigungsprotokoll und Verbrauchsmaterialien ---",
41 | "dummy_cleaning_station": "--- Reinigungsstation ---",
42 | "dummy_connection_handling": "--- Verbindung ---",
43 | "dummy_manual_control": "--- Manuelle Steuerung ---",
44 | "dummy_map_data": "--- Kartendaten, Bereiche und virtuelle Begrenzungen ---",
45 | "dummy_mopping": "--- Wischen und Wasserkasten ---",
46 | "dummy_position": "--- Position ---",
47 | "dummy_retrieve_states": "--- Status abfragen ---",
48 | "dummy_spot_areas": "--- Bereiche ---",
49 | "dummy_vacuum_power": "--- Saugleistung ---",
50 | "dummy_various_control_commands": "--- Verschiedene Steuerbefehle ---",
51 | "dummy_various_other_commands": "--- Verschiedene sonstige Befehle ---",
52 | "dummy_virtual_boundaries": "--- Virtuelle Begrenzungen ---",
53 | "dummy_voice_and_sound": "--- Sprachausgabe und Ton ---",
54 | "Edge": "► Kanten-Reinigung starten",
55 | "FindMe": "◎ Roboter suchen",
56 | "GetAdvancedMode": "↶ Erweiterten Modus abfragen",
57 | "GetAirQuality": "↶ Luftqualität abfragen (z.B. PM2.5, PM10)",
58 | "GetAutoEmpty": "↶ Auto-Entleerung abfragen",
59 | "GetBatteryState": "↶ Akkustand abfragen",
60 | "GetChargerPos": "↶ Position der Ladestation abfragen",
61 | "GetChargeState": "↶ Ladestatus abfragen",
62 | "GetCleanLogs": "↶ Reinigungsprotokoll abfragen",
63 | "GetCleanSpeed": "↶ Saugkraft abfragen",
64 | "GetCleanState": "↶ Reinigungsstatus abfragen",
65 | "GetCleanState_V2": "↶ Reinigungsstatus abfragen (V2)",
66 | "GetCleanSum": "↶ Reinigungsstatistik abfragen",
67 | "GetLifeSpan": "↶ Verbrauchsmaterialnutzung abfragen",
68 | "GetMapImage": "↶ Bild der Karte abfragen",
69 | "GetMaps": "↶ Vollständiges Objekt der Karten abfragen",
70 | "GetMapsBasic": "↶ Basis-Daten der Karte abfragen",
71 | "GetNetInfo": "↶ Netzwerk-Infos abfragen",
72 | "GetOnOff_continuous_cleaning": "↶ Kontinuierliche Reinigung abfragen (deprecated)",
73 | "GetOnOff_do_not_disturb": "↶ Nicht-Stören-Modus abfragen (deprecated)",
74 | "GetOnOff_silence_voice_report": "↶ Status der Sprachfunktion abfragen",
75 | "GetContinuousCleaning": "↶ Kontinuierliche Reinigung abfragen",
76 | "GetDoNotDisturb": "↶ Nicht-Stören-Modus abfragen",
77 | "GetPosition": "↶ Position des Roboters abfragen",
78 | "GetSchedule": "↶ Reinigungsplan abfragen",
79 | "GetSleepStatus": "↶ Standby-Modus abfragen",
80 | "GetSpotAreaInfo": "↶ Infos zu einem Bereich abfragen",
81 | "GetSpotAreas": "↶ Bereiche der Karte beziehen",
82 | "GetTrueDetect": "↶ TrueDetect abfragen",
83 | "GetVirtualBoundaries": "↶ Virtuelle Begrenzungen der Karte beziehen",
84 | "GetVirtualBoundaryInfo": "↶ Infos zu einem Virtuellen Bereich beziehen",
85 | "GetVolume": "↶ Lautstärke abfragen",
86 | "GetWaterBoxInfo": "↶ Wassertank abfragen",
87 | "GetWaterLevel": "↶ Wasserdurchflussrate abfragen",
88 | "info_950Type": "Nur 950 basierte Modelle (z.B. OZMO 920/950, N8/T8/T9/T10/X1 Serien)",
89 | "info_950Type_V2": "Nur aktuelle Modelle (z.B. N8/T8/T9/T10/X1 Serien)",
90 | "info_X1Series": "Nur X1 Serie oder ähnliche Modelle",
91 | "info_airPurifier": "Funktioniert auch für Modelle zur Luftreinigung (z.B. AIRBOT Z1)",
92 | "info_airPurifier_only": "Nur für Modelle zur Luftreinigung (z.B. AIRBOT Z1)",
93 | "info_canvasLibrary": "Die canvas Library muss installiert sein",
94 | "info_cleaningStation": "Nur Modelle mit Reinigungsstation",
95 | "info_enableSimpleEvents": "Ausgabe einfache Events aktivieren muss aktiviert sein",
96 | "info_experimental": "Experimenteller Status",
97 | "info_laserType": "Nur Modelle mit Lasersteuerung",
98 | "info_mainBrush": "Nur Modelle mit Hauptbürste",
99 | "info_moppingSystem": "Nur Modelle mit Wischfunktion",
100 | "info_non950Type": "Nur einige ältere Modelle (e.g. OZMO 930, Deebot 900/901)",
101 | "info_nonLaserType": "Nur Modelle ohne Lasersteuerung",
102 | "info_notWorking": "Wird von den meisten Modellen nicht unterstützt",
103 | "info_ozmo930": "Funktioniert nur mit dem OZMO 930",
104 | "info_suctionStation": "Nur Modelle mit Absaugstation",
105 | "info_trueDetect": "Nur Modelle mit TrueDetect 3D",
106 | "info_worksOnlyOnce": "Funktioniert nur einmal in Folge (und bei manchen Modellen gar nicht)",
107 | "label_areas": "IDs der Bereiche (z.B. 0,5)",
108 | "label_boundaryCoordinates": "Koordinaten der Begrenzung (z.B. -1320,-1100,1705,1200)",
109 | "label_boundaryID": "ID der Begrenzung (z.B. 5)",
110 | "label_boundaryType": "Art der Begrenzung
- \"vw\": virtuelle Wand
- \"mw\": Wisch-Verbotszone
",
111 | "label_command": "Auszuführender Befehl",
112 | "label_includeMapImage": "Bild der Karte inkludieren",
113 | "label_level": "Level",
114 | "label_mapID": "ID der Karte (z.B. 1286981979)",
115 | "label_mapInfoType": "Typ der Karte
- \"outline\": Umriss
- \"wifiHeatMap\": WLAN-Abdeckungskarte
",
116 | "label_mapSetID": "ID des Karten-Sets (mapSetID)",
117 | "label_name": "Name",
118 | "label_numberOfCleanings": "Anzahl der Reinigungsdurchgänge",
119 | "label_soundID": "ID des Sounds (soundID siehe hier)",
120 | "label_spotAreaID": "ID des Bereichs (z.B. 5)",
121 | "label_timeStart": "Start (Uhrzeit)",
122 | "label_timeEnd": "Ende (Uhrzeit)",
123 | "label_value": "Wert",
124 | "label_volume": "Lautstärke",
125 | "MoveBackward": "◄ Zurückfahren",
126 | "MoveForward": "► Vorwärtsfahren",
127 | "MoveLeft": "▲ Nach Links drehen",
128 | "MoveRight": "▼ Nach rechts drehen",
129 | "Pause": "⏸ Reinigung pausieren",
130 | "PlaySound_custom": "♫ Sound abspielen (manuell)",
131 | "PlaySound": "♫ Melodie abspielen",
132 | "Relocate": "◎ Positionsbestimmung des Roboters",
133 | "RenameSpotArea": "⌶ Bereich umbenennen",
134 | "ResetLifeSpan_filter": "↺ Filter zurücksetzen",
135 | "ResetLifeSpan_main_brush": "↺ Hauptbürste zurücksetzen",
136 | "ResetLifeSpan_side_brush": "↺ Seitenbürste zurücksetzen",
137 | "ResetLifeSpan_unit_care": "↺ Andere Bauteile zurücksetzen",
138 | "Resume": "► Reinigung fortsetzen",
139 | "SetAutoEmpty": "⚙ Auto-Entleerung ändern",
140 | "SetCleanSpeed": "⚙ Saugkraft anpassen",
141 | "SetOnOff_continuous_cleaning": "⚙ Kontinuierliche Reinigung setzen",
142 | "SetOnOff_do_not_disturb": "⚙ Nicht-Stören-Modus setzen (ältere Modelle)",
143 | "SetOnOff_silence_voice_report": "⚙ Sprachfunktion stummschalten",
144 | "SetVolume": "⚙ Lautstärke anpassen",
145 | "SetWaterLevel": "⚙ Wasserdurchflussrate ändern",
146 | "Spot": "► Spot-Reinigung",
147 | "SpotArea": "► Bereich(e) reinigen",
148 | "Stop": "◼ Reinigung stoppen",
149 | "WashingStart": "► Starte Selbstreinigung",
150 | "WashingStop": "◼ Stoppe Selbstreinigung"
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/resources/commands.js:
--------------------------------------------------------------------------------
1 | const commands = {
2 | "AddVirtualBoundary": {
3 | arg: {
4 | name: "mapID",
5 | required: true,
6 | type: "string_mapID"
7 | },
8 | arg2: {
9 | name: "boundaryCoordinates",
10 | required: true,
11 | type: "string"
12 | },
13 | arg3: {
14 | name: "boundaryType",
15 | required: true,
16 | type: "string_boundaryType"
17 | },
18 | info: ["950Type", "laserType"],
19 | payload: "AddVirtualBoundary"
20 | },
21 | "Charge": {
22 | payload: "Charge",
23 | info: ["airPurifier"]
24 | },
25 | "AirDryingStart": {
26 | payload: "AirDryingStart",
27 | info: ["X1Series", "cleaningStation"]
28 | },
29 | "AirDryingStop": {
30 | payload: "AirDryingStop",
31 | info: ["X1Series", "cleaningStation"]
32 | },
33 | "Clean": {
34 | payload: "Clean"
35 | },
36 | "Clean_V2": {
37 | payload: "Clean_V2",
38 | info: ["950Type_V2", "airPurifier"]
39 | },
40 | "Connect": {
41 | payload: "Connect"
42 | },
43 | "CustomArea": {
44 | arg: "start",
45 | arg2: {
46 | name: "boundaryCoordinates",
47 | required: true,
48 | type: "string"
49 | },
50 | arg3: {
51 | name: "numberOfCleanings",
52 | required: false,
53 | type: "number_cleanings"
54 | },
55 | info: ["laserType"],
56 | payload: "CustomArea"
57 | },
58 | "DeleteVirtualBoundary": {
59 | arg: {
60 | name: "mapID",
61 | required: true,
62 | type: "string_mapID"
63 | },
64 | arg2: {
65 | name: "boundaryID",
66 | required: true,
67 | type: "string"
68 | },
69 | arg3: {
70 | name: "boundaryType",
71 | required: true,
72 | type: "string_boundaryType"
73 | },
74 | info: ["950Type", "laserType"],
75 | payload: "DeleteVirtualBoundary"
76 | },
77 | "DisableAdvancedMode": {
78 | info: ["950Type"],
79 | payload: "DisableAdvancedMode"
80 | },
81 | "DisableAutoEmpty": {
82 | info: ["suctionStation", "950Type_V2"],
83 | payload: "DisableAutoEmpty"
84 | },
85 | "DisableBorderSpin": {
86 | info: ["X1Series"],
87 | payload: "EnableBorderSpin"
88 | },
89 | "DisableCarpetPressure": {
90 | info: ["950Type"],
91 | payload: "DisableCarpetPressure"
92 | },
93 | "DisableCleanPreference": {
94 | info: ["950Type_V2"],
95 | payload: "DisableCleanPreference"
96 | },
97 | "DisableDoNotDisturb": {
98 | payload: "DisableDoNotDisturb"
99 | },
100 | "DisableSweepMode": {
101 | info: ["X1Series"],
102 | payload: "EnableSweepMode"
103 | },
104 | "DisableTrueDetect": {
105 | info: ["trueDetect", "950Type_V2"],
106 | payload: "DisableTrueDetect"
107 | },
108 | "DisableVoiceAssistant": {
109 | info: ["X1Series", "airPurifier"],
110 | payload: "EnableVoiceAssistant"
111 | },
112 | "Disconnect": {
113 | payload: "Disconnect"
114 | },
115 | "EnableAdvancedMode": {
116 | info: ["950Type"],
117 | payload: "EnableAdvancedMode"
118 | },
119 | "EnableAutoEmpty": {
120 | info: ["suctionStation", "950Type_V2"],
121 | payload: "EnableAutoEmpty"
122 | },
123 | "EnableBorderSpin": {
124 | info: ["X1Series"],
125 | payload: "EnableBorderSpin"
126 | },
127 | "EnableCarpetPressure": {
128 | info: ["950Type"],
129 | payload: "EnableCarpetPressure"
130 | },
131 | "EnableCleanPreference": {
132 | info: ["950Type_V2"],
133 | payload: "EnableCleanPreference"
134 | },
135 | "EnableDoNotDisturb": {
136 | arg: {
137 | name: "timeStart",
138 | required: true,
139 | type: "time"
140 | },
141 | arg2: {
142 | name: "timeEnd",
143 | required: true,
144 | type: "string"
145 | },
146 | info: ["950Type"],
147 | payload: "EnableDoNotDisturb"
148 | },
149 | "EnableSweepMode": {
150 | info: ["X1Series"],
151 | payload: "EnableSweepMode"
152 | },
153 | "EnableTrueDetect": {
154 | info: ["trueDetect", "950Type_V2"],
155 | payload: "EnableTrueDetect"
156 | },
157 | "EnableVoiceAssistant": {
158 | info: ["X1Series", "airPurifier"],
159 | payload: "EnableVoiceAssistant"
160 | },
161 | "Edge": {
162 | info: ["nonLaserType"],
163 | payload: "Edge"
164 | },
165 | "FindMe": {
166 | arg: "30",
167 | label: "FindMe",
168 | payload: "PlaySound",
169 | info: ["airPurifier"]
170 | },
171 | "GetAirQuality": {
172 | info: ["airPurifier_only"],
173 | payload: "GetAirQuality"
174 | },
175 | "GetAutoEmpty": {
176 | info: ["suctionStation", "950Type_V2"],
177 | payload: "GetAutoEmpty"
178 | },
179 | "GetAdvancedMode": {
180 | info: ["950Type"],
181 | payload: "GetAdvancedMode"
182 | },
183 | "GetBatteryState": {
184 | payload: "GetBatteryState",
185 | info: ["airPurifier"]
186 | },
187 | "GetChargerPos": {
188 | info: ["laserType"],
189 | payload: "GetChargerPos"
190 | },
191 | "GetChargeState": {
192 | payload: "GetChargeState",
193 | info: ["airPurifier"]
194 | },
195 | "GetCleanLogs": {
196 | payload: "GetCleanLogs"
197 | },
198 | "GetCleanSpeed": {
199 | payload: "GetCleanSpeed"
200 | },
201 | "GetCleanState": {
202 | payload: "GetCleanState"
203 | },
204 | "GetCleanState_V2": {
205 | payload: "GetCleanState_V2",
206 | info: ["950Type_V2", "airPurifier"]
207 | },
208 | "GetCleanSum": {
209 | payload: "GetCleanSum",
210 | info: ["airPurifier"]
211 | },
212 | "GetLifeSpan": {
213 | payload: "GetLifeSpan"
214 | },
215 | "GetMapImage": {
216 | arg: {
217 | name: "mapID",
218 | required: true,
219 | type: "string_mapID"
220 | },
221 | arg2: {
222 | name: "mapInfoType",
223 | required: true,
224 | type: "string_mapInfoType"
225 | },
226 | info: ["950Type", "laserType", "canvasLibrary"],
227 | payload: "GetMapImage"
228 | },
229 | "GetMaps": {
230 | arg: true,
231 | arg2: {
232 | name: "includeMapImage",
233 | required: true,
234 | type: "boolean"
235 | },
236 | info: ["laserType", "canvasLibrary"],
237 | payload: "GetMaps"
238 | },
239 | "GetMapsBasic": {
240 | info: ["laserType", "enableSimpleEvents"],
241 | payload: "GetMaps"
242 | },
243 | "GetNetInfo": {
244 | payload: "GetNetInfo",
245 | info: ["airPurifier"]
246 | },
247 | "GetOnOff_continuous_cleaning": {
248 | arg: "continuous_cleaning",
249 | label: "GetOnOff (continuous cleaning)",
250 | payload: "GetOnOff"
251 | },
252 | "GetOnOff_do_not_disturb": {
253 | arg: "do_not_disturb",
254 | label: "GetOnOff (do not disturb)",
255 | payload: "GetOnOff"
256 | },
257 | "GetOnOff_silence_voice_report": {
258 | arg: "silence_voice_report",
259 | label: "GetOnOff (silence voice report)",
260 | info: ["ozmo930"],
261 | payload: "GetOnOff"
262 | },
263 | "GetPosition": {
264 | info: ["laserType", "airPurifier"],
265 | payload: "GetPosition"
266 | },
267 | "GetSchedule": {
268 | payload: "GetSchedule"
269 | },
270 | "GetSleepStatus": {
271 | payload: "GetSleepStatus",
272 | info: ["airPurifier"]
273 | },
274 | "GetSpotAreaInfo": {
275 | arg: {
276 | name: "mapID",
277 | required: true,
278 | type: "string_mapID"
279 | },
280 | arg2: {
281 | name: "spotAreaID",
282 | required: true,
283 | type: "string"
284 | },
285 | info: ["laserType", "enableSimpleEvents"],
286 | payload: "GetSpotAreaInfo"
287 | },
288 | "GetSpotAreas": {
289 | arg: {
290 | name: "mapID",
291 | required: true,
292 | type: "string_mapID"
293 | },
294 | info: ["laserType", "enableSimpleEvents"],
295 | payload: "GetSpotAreas"
296 | },
297 | "GetTrueDetect": {
298 | info: ["trueDetect", "950Type_V2", "airPurifier"],
299 | payload: "GetTrueDetect"
300 | },
301 | "GetVirtualBoundaries": {
302 | arg: {
303 | name: "mapID",
304 | required: true,
305 | type: "string_mapID"
306 | },
307 | info: ["laserType", "enableSimpleEvents"],
308 | payload: "GetVirtualBoundaries"
309 | },
310 | "GetVirtualBoundaryInfo": {
311 | arg: {
312 | name: "mapID",
313 | required: true,
314 | type: "string_mapID"
315 | },
316 | arg2: {
317 | name: "boundaryID",
318 | required: true,
319 | type: "string"
320 | },
321 | arg3: {
322 | name: "boundaryType",
323 | required: true,
324 | type: "string_boundaryType"
325 | },
326 | info: ["laserType", "enableSimpleEvents"],
327 | payload: "GetVirtualBoundaryInfo"
328 | },
329 | "GetVolume": {
330 | label: "GetVolume",
331 | info: ["950Type"],
332 | payload: "GetVolume"
333 | },
334 | "GetWaterBoxInfo": {
335 | info: ["moppingSystem"],
336 | payload: "GetWaterBoxInfo"
337 | },
338 | "GetWaterLevel": {
339 | info: ["moppingSystem"],
340 | payload: "GetWaterLevel"
341 | },
342 | "MoveBackward": {
343 | info: ["worksOnlyOnce", "airPurifier"],
344 | payload: "MoveBackward"
345 | },
346 | "MoveForward": {
347 | info: ["worksOnlyOnce", "airPurifier"],
348 | payload: "MoveForward"
349 | },
350 | "MoveLeft": {
351 | info: ["worksOnlyOnce", "airPurifier"],
352 | payload: "MoveLeft"
353 | },
354 | "MoveRight": {
355 | info: ["worksOnlyOnce", "airPurifier"],
356 | payload: "MoveRight"
357 | },
358 | "Pause": {
359 | info: ["airPurifier"],
360 | payload: "Pause"
361 | },
362 | "PlaySound_custom": {
363 | arg: {
364 | name: "soundID",
365 | required: true,
366 | type: "string"
367 | },
368 | info: ["airPurifier"],
369 | label: "PlaySound (soundID)",
370 | payload: "PlaySound"
371 | },
372 | "PlaySound": {
373 | info: ["airPurifier"],
374 | payload: "PlaySound"
375 | },
376 | "Relocate": {
377 | info: ["950Type", "airPurifier"],
378 | payload: "Relocate"
379 | },
380 | "RenameSpotArea": {
381 | arg: {
382 | name: "mapSetID",
383 | required: true,
384 | type: "string"
385 | },
386 | arg2: {
387 | name: "spotAreaID",
388 | required: true,
389 | type: "string"
390 | },
391 | arg3: {
392 | name: "name",
393 | required: true,
394 | type: "string"
395 | },
396 | info: ["ozmo930"],
397 | payload: "RenameSpotArea"
398 | },
399 | "ResetLifeSpan_filter": {
400 | arg: "filter",
401 | label: "ResetLifeSpan (filter)",
402 | payload: "ResetLifeSpan"
403 | },
404 | "ResetLifeSpan_main_brush": {
405 | arg: "main_brush",
406 | label: "ResetLifeSpan (main brush)",
407 | info: ["mainBrush"],
408 | payload: "ResetLifeSpan"
409 | },
410 | "ResetLifeSpan_side_brush": {
411 | arg: "side_brush",
412 | label: "ResetLifeSpan (side brush)",
413 | payload: "ResetLifeSpan"
414 | },
415 | "ResetLifeSpan_unit_care": {
416 | arg: "unit_care",
417 | label: "ResetLifeSpan (unit care)",
418 | info: ["950Type_V2"],
419 | payload: "ResetLifeSpan"
420 | },
421 | "Resume": {
422 | info: ["airPurifier"],
423 | payload: "Resume"
424 | },
425 | "SetAutoEmpty": {
426 | arg: {
427 | name: "value",
428 | required: true,
429 | type: "number_on_off"
430 | },
431 | info: ["suctionStation", "950Type_V2"],
432 | payload: "SetAutoEmpty"
433 | },
434 | "SetCleanSpeed": {
435 | arg: {
436 | name: "level",
437 | required: true,
438 | type: "number_level"
439 | },
440 | payload: "SetCleanSpeed"
441 | },
442 | "SetOnOff_continuous_cleaning": {
443 | arg: "continuous_cleaning",
444 | arg2: {
445 | name: "value",
446 | required: true,
447 | type: "number_on_off"
448 | },
449 | label: "SetOnOff (continuous cleaning)",
450 | info: ["non950Type"],
451 | payload: "SetOnOff"
452 | },
453 | "SetOnOff_do_not_disturb": {
454 | arg: "do_not_disturb",
455 | arg2: {
456 | name: "value",
457 | required: true,
458 | type: "number_on_off"
459 | },
460 | label: "SetOnOff (do not disturb)",
461 | info: ["non950Type"],
462 | payload: "SetOnOff"
463 | },
464 | "SetOnOff_silence_voice_report": {
465 | arg: "silence_voice_report",
466 | arg2: {
467 | name: "value",
468 | required: true,
469 | type: "number_on_off"
470 | },
471 | label: "SetOnOff (silence voice report)",
472 | info: ["ozmo930"],
473 | payload: "SetOnOff"
474 | },
475 | "SetVolume": {
476 | arg: {
477 | name: "volume",
478 | required: true,
479 | type: "number_volume"
480 | },
481 | info: ["950Type"],
482 | payload: "SetVolume"
483 | },
484 | "SetWaterLevel": {
485 | arg: {
486 | name: "level",
487 | required: true,
488 | type: "number_level"
489 | },
490 | info: ["moppingSystem"],
491 | payload: "SetWaterLevel"
492 | },
493 | "Spot": {
494 | info: ["nonLaserType"],
495 | payload: "Spot"
496 | },
497 | "SpotArea": {
498 | arg: "start",
499 | arg2: {
500 | name: "areas",
501 | required: true,
502 | type: "string"
503 | },
504 | info: ["laserType"],
505 | payload: "SpotArea"
506 | },
507 | "WashingStart": {
508 | info: ["X1Series", "cleaningStation"],
509 | payload: "WashingStart"
510 | },
511 | "WashingStop": {
512 | info: ["X1Series", "cleaningStation"],
513 | payload: "WashingStop"
514 | }
515 | };
516 |
517 | function getArgName(command, argNumber) {
518 | if (!hasArg(command, argNumber)) {
519 | return "";
520 | }
521 | if (commands[command]["arg" + getSuffix(argNumber)].hasOwnProperty("name")) {
522 | return commands[command]["arg" + getSuffix(argNumber)]["name"];
523 | }
524 | return "";
525 | }
526 |
527 | function getArgType(command, argNumber) {
528 | if (!hasArg(command, argNumber)) {
529 | return "string";
530 | }
531 | if (commands[command]["arg" + getSuffix(argNumber)].hasOwnProperty("type")) {
532 | return commands[command]["arg" + getSuffix(argNumber)]["type"];
533 | }
534 | return "string";
535 | }
536 |
537 | function getSuffix(argNumber) {
538 | if (argNumber > 1) {
539 | return argNumber;
540 | }
541 | return '';
542 | }
543 |
544 | function getTranslation(node, text) {
545 | if (isTranslationAvailable(node, text)) {
546 | return node._(node.type + "." + text);
547 | }
548 | return "";
549 | }
550 |
551 | function hasArg(command, argNumber) {
552 | if (!isValidArgNumber || !isValidCommand(command)) {
553 | return false;
554 | }
555 | return commands[command].hasOwnProperty("arg" + getSuffix(argNumber));
556 |
557 | }
558 |
559 | function isArgRequired(command, argNumber, ignoreRequired = false) {
560 | if (!hasArg(command, argNumber)) {
561 | return false;
562 | }
563 | if (typeof commands[command]['arg' + getSuffix(argNumber)] !== "object") {
564 | return false;
565 | }
566 | if (commands[command]['arg' + getSuffix(argNumber)].hasOwnProperty("required") && !ignoreRequired) {
567 | return commands[command]['arg' + getSuffix(argNumber)]["required"];
568 | }
569 | return true;
570 | }
571 |
572 | function getArgValue(command, arg, number) {
573 | if (getArgType(command, number).substring(0, 6) === "number") {
574 | return parseInt(arg);
575 | } else if (getArgType(command, number) === "boolean") {
576 | return arg.toLowerCase() === "true";
577 | } else {
578 | return arg;
579 | }
580 | }
581 |
582 | function isTranslationAvailable(node, text) {
583 | return node._(node.type + "." + text) !== (node.type + "." + text);
584 | }
585 |
586 | function isValidArg(command, argNumber, argValue) {
587 | if (!hasArg(command, argNumber)) {
588 | return true;
589 | }
590 | let myRegEx;
591 | switch (getArgName(command, argNumber)) {
592 | case "areas":
593 | myRegEx = new RegExp('^[\\d\\,]+\\;?$');
594 | break;
595 | case "boundaryCoordinates":
596 | myRegEx = new RegExp('^(((-?\\d+(\\.(\\d*))?)\\,(-?\\d+(\\.(\\d*))?))+\\,)*((-?\\d+(\\.(\\d*))?)\\,(-?\\d+(\\.(\\d*))?))+[\\;\\,]?$');
597 | break;
598 | case "boundaryID":
599 | case "spotAreaID":
600 | case "mapSetID":
601 | myRegEx = new RegExp('^\\d+$');
602 | break;
603 | case "boundaryType":
604 | myRegEx = new RegExp('^(mw|vw)$');
605 | break;
606 | case "level":
607 | myRegEx = new RegExp('^[1234]$');
608 | break;
609 | case "mapID":
610 | myRegEx = new RegExp('^\\d{7,}$');
611 | break;
612 | case "mapInfoType":
613 | myRegEx = new RegExp('^(outline|wifiHeatMap)$');
614 | break;
615 | case "numberOfCleanings":
616 | myRegEx = new RegExp('^[12]$');
617 | break;
618 | case "soundID":
619 | myRegEx = new RegExp('^(20[01]|(1?\\d)?\\d)$');
620 | break;
621 | case "value":
622 | myRegEx = new RegExp('^[01]$');
623 | break;
624 | case "volume":
625 | myRegEx = new RegExp('^(\\d|10)$');
626 | break;
627 | case "timeStart":
628 | case "timeEnd":
629 | myRegEx = new RegExp('^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$');
630 | break;
631 | default:
632 | switch (getArgType(command, argNumber)) {
633 | case "string":
634 | myRegEx = new RegExp('^[a-zA-Z\\d_]+$');
635 | break;
636 | case "number":
637 | myRegEx = new RegExp('^\\d+$');
638 | break;
639 | default:
640 | return true;
641 | }
642 | }
643 | return myRegEx.test(argValue);
644 | }
645 |
646 | function isValidArgNumber(argNumber) {
647 | return (!(argNumber < 0) && !(argNumber > 3));
648 | }
649 |
650 | function isValidCommand(command) {
651 | return commands.hasOwnProperty(command);
652 | }
653 |
654 | module.exports = {commands, getArgValue};
655 |
--------------------------------------------------------------------------------
/nodes/ecovacs-deebot.js:
--------------------------------------------------------------------------------
1 | module.exports = function (RED) {
2 | const library = require('ecovacs-deebot');
3 | const EcoVacsAPI = library.EcoVacsAPI;
4 | const nodeMachineId = require('node-machine-id');
5 | const countries = library.countries;
6 | const packageInfo = require('../package.json');
7 |
8 | function connect(node) {
9 | node.vacbot = null;
10 |
11 | node.status({
12 | fill: 'yellow',
13 | shape: 'dot',
14 | text: 'Connecting...',
15 | });
16 |
17 | const deviceNumber = node.config.deviceNumber - 1
18 | const password_hash = EcoVacsAPI.md5(node.account.password);
19 | const device_id = EcoVacsAPI.getDeviceId(nodeMachineId.machineIdSync(), deviceNumber);
20 | const countryCode = node.account.countryCode.toLowerCase();
21 | const continent = countries[countryCode.toUpperCase()].continent.toLowerCase();
22 | let login = '';
23 | if (node.account.login) {
24 | login = node.account.login;
25 | }
26 |
27 | let api = new EcoVacsAPI(device_id, countryCode, continent, login);
28 | api.connect(node.account.email, password_hash).then(() => {
29 | api.devices().then((devices) => {
30 | let vacuum = devices[deviceNumber];
31 | node.vacbot = api.getVacBot(api.uid, EcoVacsAPI.REALM, api.resource, api.user_access_token, vacuum, continent);
32 | node.vacbot.on('ready', (event) => {
33 | node.status({
34 | fill: 'green',
35 | shape: 'dot',
36 | text: 'Connected',
37 | });
38 |
39 | const nickname = vacuum.nick ? vacuum.nick : 'New Device ' + this.deviceNumber;
40 | node.send({
41 | payload: {
42 | "type": "info",
43 | "value": {
44 | "nickname": nickname,
45 | "version": packageInfo.version,
46 | "libraryVersion": api.getVersion(),
47 | "deviceClass": node.vacbot.deviceClass,
48 | "deviceModel": node.vacbot.deviceModel
49 | }
50 | }
51 | });
52 |
53 | // aqMonitor = e.g. Z1 Air Quality Monitor
54 | if (node.vacbot.getModelType() !== 'aqMonitor') {
55 | node.vacbot.run('GetBatteryState');
56 | // airbot = e.g. AIRBOT Z1
57 | if (node.vacbot.getModelType() === 'airbot') {
58 | node.vacbot.run('GetCleanState_V2');
59 | } else {
60 | node.vacbot.run('GetCleanState');
61 | }
62 | node.vacbot.run('GetChargeState');
63 | if (node.vacbot.getModelType() !== 'airbot') {
64 | node.vacbot.run('GetSleepStatus');
65 | node.vacbot.run('GetCleanSum');
66 | node.vacbot.run('GetCleanLogs');
67 | node.vacbot.run('GetLifespan');
68 | }
69 | if (node.vacbot.hasSpotAreaCleaningMode()) {
70 | node.vacbot.run('GetPosition');
71 | if (node.vacbot.isNot950type()) {
72 | // On 950 type models not necessary
73 | node.vacbot.run('GetChargerPos');
74 | }
75 | if (node.config.enableGetMapsOnStartup) {
76 | node.vacbot.run('GetMaps', true, true);
77 | }
78 | }
79 | if (node.vacbot.hasVacuumPowerAdjustment()) {
80 | node.vacbot.run('GetCleanSpeed');
81 | }
82 | if (node.vacbot.hasMoppingSystem()) {
83 | node.vacbot.run('GetWaterBoxInfo');
84 | if (node.vacbot.isNot950type()) {
85 | // On 950 type models not necessary
86 | node.vacbot.run('GetWaterLevel');
87 | }
88 | }
89 | }
90 |
91 | node.vacbot.on('disconnect', (error) => {
92 | if (error) {
93 | const errorMessage = 'Received disconnect event from library';
94 | node.vacbot.disconnect();
95 | node.status({
96 | fill: 'gray',
97 | shape: 'dot',
98 | text: errorMessage
99 | });
100 | node.error(errorMessage);
101 | }
102 | });
103 | node.vacbot.on('BatteryInfo', (value) => {
104 | const msg = createMsgObject('BatteryInfo', value , null, '%');
105 | node.send(msg);
106 | });
107 | node.vacbot.on('CleanReport', (value) => {
108 | const msg = createMsgObject('CleanReport', value);
109 | node.send(msg);
110 | });
111 | node.vacbot.on('ChargeState', (value) => {
112 | const msg = createMsgObject('ChargeState', value);
113 | node.send(msg);
114 | });
115 | node.vacbot.on('LifeSpan', (object) => {
116 | const msg = createMsgObject('LifeSpan', object);
117 | node.send(msg);
118 | });
119 | node.vacbot.on('Position', (object) => {
120 | const msg = createMsgObject('Position', object);
121 | node.send(msg);
122 | });
123 | node.vacbot.on('ChargingPosition', (object) => {
124 | const msg = createMsgObject('ChargingPosition', object);
125 | node.send(msg);
126 | });
127 | node.vacbot.on('LastError', (object) => {
128 | const msg = createMsgObject('LastError', object);
129 | node.send(msg);
130 | });
131 | node.vacbot.on('MoppingSystemInfo', (object) => {
132 | const msg = createMsgObject('MoppingSystemInfo', object);
133 | node.send(msg);
134 | });
135 | node.vacbot.on('WaterBoxInfo', (value) => {
136 | const msg = createMsgObject('WaterBoxInfo', value);
137 | node.send(msg);
138 | });
139 | node.vacbot.on('WaterLevel', (value) => {
140 | const msg = createMsgObject('WaterLevel', value);
141 | node.send(msg);
142 | });
143 | node.vacbot.on('DustCaseInfo', (value) => {
144 | const msg = createMsgObject('DustCaseInfo', value);
145 | node.send(msg);
146 | });
147 | node.vacbot.on('SleepStatus', (value) => {
148 | const msg = createMsgObject('SleepStatus', value);
149 | node.send(msg);
150 | });
151 | node.vacbot.on('CleanSpeed', (value) => {
152 | const msg = createMsgObject('CleanSpeed', value);
153 | node.send(msg);
154 | });
155 | node.vacbot.on('CleanLog', (object) => {
156 | const msg = createMsgObject('CleanLog', object);
157 | node.send(msg);
158 | });
159 | node.vacbot.on('LastCleanLogs', (object) => {
160 | const msg = createMsgObject('LastCleanLogs', object);
161 | node.send(msg);
162 | });
163 | node.vacbot.on('NetworkInfo', (object) => {
164 | const msg = createMsgObject('NetworkInfo', object);
165 | node.send(msg);
166 | });
167 | node.vacbot.on('CleanSum', (object) => {
168 | const msg = createMsgObject('CleanSum', object);
169 | node.send(msg);
170 | });
171 | node.vacbot.on('Volume', (value) => {
172 | const msg = createMsgObject('Volume', value);
173 | node.send(msg);
174 | });
175 | node.vacbot.on('AutoEmpty', (value) => {
176 | const msg = createMsgObject('AutoEmpty', value);
177 | node.send(msg);
178 | });
179 | node.vacbot.on('AdvancedMode', (value) => {
180 | const msg = createMsgObject('AdvancedMode', value);
181 | node.send(msg);
182 | });
183 | node.vacbot.on('CarpetPressure', (value) => {
184 | const msg = createMsgObject('CarpetPressure', value);
185 | node.send(msg);
186 | });
187 | node.vacbot.on('CleanPreference', (value) => {
188 | const msg = createMsgObject('CleanPreference', value);
189 | node.send(msg);
190 | });
191 | node.vacbot.on('DusterRemind', (object) => {
192 | const msg = createMsgObject('DusterRemind', object);
193 | node.send(msg);
194 | });
195 | node.vacbot.on('WaterBoxMoppingType', (value) => {
196 | const msg = createMsgObject('WaterBoxMoppingType', value);
197 | node.send(msg);
198 | });
199 | node.vacbot.on('WaterBoxScrubbingType', (value) => {
200 | const msg = createMsgObject('WaterBoxScrubbingType', value);
201 | node.send(msg);
202 | });
203 | node.vacbot.on('TrueDetect', (value) => {
204 | const msg = createMsgObject('TrueDetect', value);
205 | node.send(msg);
206 | });
207 | node.vacbot.on('DoNotDisturbEnabled', (value) => {
208 | const doNotDisturb = (parseInt(value) === 1);
209 | const msg = createMsgObject('DoNotDisturbEnabled', doNotDisturb);
210 | node.send(msg);
211 | });
212 | node.vacbot.on('ContinuousCleaningEnabled', (value) => {
213 | const continuousCleaning = (parseInt(value) === 1);
214 | const msg = createMsgObject('ContinuousCleaningEnabled', continuousCleaning);
215 | node.send(msg);
216 | });
217 | node.vacbot.on('VoiceReportDisabled', (value) => {
218 | const voiceReportDisabled = (parseInt(value) === 1);
219 | const msg = createMsgObject('VoiceReportDisabled', voiceReportDisabled);
220 | node.send(msg);
221 | });
222 | node.vacbot.on('LastUsedAreaValues', (value) => {
223 | const msg = createMsgObject('LastUsedAreaValues', value);
224 | node.send(msg);
225 | });
226 | node.vacbot.on('MapDataObject', (object) => {
227 | const msg = createMsgObject('MapDataObject', object);
228 | node.send(msg);
229 | });
230 | node.vacbot.on('MapImage', (object) => {
231 | const msg = createMsgObject('MapImage', object);
232 | node.send(msg);
233 | });
234 | node.vacbot.on('CurrentMapName', (value) => {
235 | const msg = createMsgObject('CurrentMapName', value);
236 | node.send(msg);
237 | });
238 | node.vacbot.on('CurrentMapMID', (value) => {
239 | const msg = createMsgObject('CurrentMapMID', value);
240 | node.send(msg);
241 | });
242 | node.vacbot.on('CurrentMapIndex', (value) => {
243 | const msg = createMsgObject('CurrentMapIndex', value);
244 | node.send(msg);
245 | });
246 | node.vacbot.on('CurrentCustomAreaValues', (value) => {
247 | const msg = createMsgObject('CurrentUsedCustomAreaValues', value);
248 | node.send(msg);
249 | });
250 | node.vacbot.on('CurrentSpotAreas', (value) => {
251 | const msg = createMsgObject('CurrentUsedSpotAreas', value);
252 | node.send(msg);
253 | });
254 | node.vacbot.on('DeebotPositionCurrentSpotAreaID', (value) => {
255 | const msg = createMsgObject('CurrentSpotAreaID', value);
256 | node.send(msg);
257 | });
258 | node.vacbot.on('RelocationState', (value) => {
259 | const msg = createMsgObject('RelocationState', value);
260 | node.send(msg);
261 | });
262 | node.vacbot.on('CleanCount', (value) => {
263 | const msg = createMsgObject('CleanCount', value);
264 | node.send(msg);
265 | });
266 | node.vacbot.on('Schedule', (object) => {
267 | const msg = createMsgObject('Schedule', object);
268 | node.send(msg);
269 | });
270 | node.vacbot.on('AirQuality', (object) => {
271 | const msg = createMsgObject('AirQuality', object);
272 | node.send(msg);
273 | });
274 | // Activate additional simple events if enabled
275 | if (node.config.enableSimpleEvents) {
276 | node.vacbot.on('ChargePosition', (value) => {
277 | const msg = createMsgObject('ChargePosition', value);
278 | node.send(msg);
279 | });
280 | node.vacbot.on('DeebotPosition', (value) => {
281 | const msg = createMsgObject('DeebotPosition', value);
282 | node.send(msg);
283 | });
284 | node.vacbot.on('CleanLog_lastImageTimestamp', (value) => {
285 | const msg = createMsgObject('CleanLog_lastImageTimestamp', value);
286 | node.send(msg);
287 | });
288 | node.vacbot.on('CleanLog_lastImageUrl', (value) => {
289 | const msg = createMsgObject('CleanLog_lastImageUrl', value);
290 | node.send(msg);
291 | });
292 | node.vacbot.on('CleanLog_lastSquareMeters', (value) => {
293 | const msg = createMsgObject('CleanLog_lastSquareMeters', value);
294 | node.send(msg);
295 | });
296 | node.vacbot.on('CleanLog_lastTimestamp', (value) => {
297 | const msg = createMsgObject('CleanLog_lastTimestamp', value);
298 | node.send(msg);
299 | });
300 | node.vacbot.on('CleanLog_lastTotalTimeString', (value) => {
301 | const msg = createMsgObject('CleanLog_lastTotalTimeString', value);
302 | node.send(msg);
303 | });
304 | node.vacbot.on('CleanSum_totalNumber', (value) => {
305 | const msg = createMsgObject('CleanSum_totalNumber', value);
306 | node.send(msg);
307 | });
308 | node.vacbot.on('CleanSum_totalSeconds', (value) => {
309 | const msg = createMsgObject('CleanSum_totalSeconds', value);
310 | node.send(msg);
311 | });
312 | node.vacbot.on('CleanSum_totalSquareMeters', (value) => {
313 | const msg = createMsgObject('CleanSum_totalSquareMeters', value);
314 | node.send(msg);
315 | });
316 | node.vacbot.on('DeebotPositionIsInvalid', (value) => {
317 | const msg = createMsgObject('DeebotPositionIsInvalid', value);
318 | node.send(msg);
319 | });
320 | node.vacbot.on('Error', (value) => {
321 | const msg = createMsgObject('Error', value);
322 | node.send(msg);
323 | });
324 | node.vacbot.on('ErrorCode', (value) => {
325 | const msg = createMsgObject('ErrorCode', value);
326 | node.send(msg);
327 | });
328 | node.vacbot.on('Maps', (object) => {
329 | const msg = createMsgObject('Maps', object);
330 | node.send(msg);
331 | });
332 | node.vacbot.on('MapSpotAreas', (object) => {
333 | const msg = createMsgObject('MapSpotAreas', object);
334 | node.send(msg);
335 | });
336 | node.vacbot.on('MapSpotAreaInfo', (object) => {
337 | const msg = createMsgObject('MapSpotAreaInfo', object);
338 | node.send(msg);
339 | });
340 | node.vacbot.on('MapVirtualBoundaries', (object) => {
341 | const msg = createMsgObject('MapVirtualBoundaries', object);
342 | node.send(msg);
343 | });
344 | node.vacbot.on('MapVirtualBoundaryInfo', (object) => {
345 | const msg = createMsgObject('MapVirtualBoundaryInfo', object);
346 | node.send(msg);
347 | });
348 | node.vacbot.on('NetInfoIP', (value) => {
349 | const msg = createMsgObject('NetInfoIP', value);
350 | node.send(msg);
351 | });
352 | node.vacbot.on('NetInfoMAC', (value) => {
353 | const msg = createMsgObject('NetInfoMAC', value);
354 | node.send(msg);
355 | });
356 | node.vacbot.on('NetInfoWifiSignal', (value) => {
357 | const msg = createMsgObject('NetInfoWifiSignal', value);
358 | node.send(msg);
359 | });
360 | node.vacbot.on('NetInfoWifiSSID', (value) => {
361 | const msg = createMsgObject('NetInfoWifiSSID', value);
362 | node.send(msg);
363 | });
364 | }
365 | });
366 | node.vacbot.connect();
367 | });
368 | }).catch((e) => {
369 | node.status({
370 | fill: 'red',
371 | shape: 'dot',
372 | text: e.error,
373 | });
374 | node.error(e.error);
375 | });
376 | }
377 |
378 | function createMsgObject(type, value, component = null, unit = null) {
379 | const output = {
380 | type: type,
381 | value: value
382 | };
383 | if (component) {
384 | Object.assign(output, {component: component});
385 | }
386 | if (unit) {
387 | Object.assign(output, {unit: unit});
388 | }
389 | return {
390 | payload: output
391 | };
392 | }
393 |
394 | function EcovacsDeebot(config) {
395 | RED.nodes.createNode(this, config);
396 |
397 | let node = this;
398 | node.config = config;
399 | node.account = RED.nodes.getNode(config.account);
400 |
401 | node.status({
402 | fill: 'gray',
403 | shape: 'dot',
404 | text: 'Not connected yet',
405 | });
406 |
407 | if (node.config.connectOnStartup) {
408 | connect(node);
409 | }
410 |
411 | node.on('input', (msg) => {
412 | if (msg.payload === 'Connect') {
413 | if (node.account) {
414 | connect(node);
415 | } else {
416 | node.status({
417 | fill: 'red',
418 | shape: 'dot',
419 | text: 'Ecovacs account missing'
420 | });
421 | }
422 | }
423 | else if (node.vacbot) {
424 | if (msg.payload === 'Disconnect') {
425 | node.vacbot.disconnect();
426 | node.status({
427 | fill: 'gray',
428 | shape: 'dot',
429 | text: 'Disconnected'
430 | });
431 | } else if ((msg.arg !== undefined) && (msg.arg2 !== undefined) && (msg.arg3 !== undefined)) {
432 | node.vacbot.run(msg.payload, msg.arg, msg.arg2, msg.arg3);
433 | } else if ((msg.arg !== undefined) && (msg.arg2 !== undefined)) {
434 | node.vacbot.run(msg.payload, msg.arg, msg.arg2);
435 | } else if ((msg.arg !== undefined)) {
436 | node.vacbot.run(msg.payload, msg.arg);
437 | } else {
438 | node.vacbot.run(msg.payload);
439 | }
440 | }
441 | });
442 |
443 | node.on('close', () => {
444 | node.vacbot.disconnect();
445 | });
446 | }
447 |
448 | RED.nodes.registerType('ecovacs-deebot', EcovacsDeebot);
449 | }
450 |
--------------------------------------------------------------------------------
/nodes/deebot-command.html:
--------------------------------------------------------------------------------
1 |
9 |
310 |
311 |
443 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------