node-red-contrib-alice 2.2.4 → 2.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,7 +16,9 @@
16
16
  types: [
17
17
  {
18
18
  options: [
19
- { value: "devices.types.light", label: "Light" },
19
+ { value: "devices.types.light", label: "Light" },
20
+ { value: "devices.types.light.ceiling", label: "Light Сeiling" },
21
+ { value: "devices.types.light.strip", label: "Light Strip" },
20
22
  { value: "devices.types.socket", label: "Socket" },
21
23
  { value: "devices.types.switch", label: "Switch" },
22
24
  { value: "devices.types.thermostat", label: "Thermostat" },
@@ -32,8 +34,11 @@
32
34
  { value: "devices.types.cooking.multicooker", label: "Multicooker" },
33
35
  { value: "devices.types.openable", label: "Door, gate, window, shutters" },
34
36
  { value: "devices.types.openable.curtain", label: "Curtains, blinds" },
37
+ { value: "devices.types.openable.valve", label: "Valve (ball valve)" },
35
38
  { value: "devices.types.humidifier", label: "Humidifier" },
36
39
  { value: "devices.types.purifier", label: "Air purifier" },
40
+ { value: "devices.types.ventilation", label: "Ventilation" },
41
+ { value: "devices.types.ventilation.fan", label: "Fan" },
37
42
  { value: "devices.types.vacuum_cleaner", label: "Vacuum cleaner robot" },
38
43
  { value: "devices.types.washing_machine", label: "Washing machine" },
39
44
  { value: "devices.types.dishwasher", label: "Dishwasher" },
@@ -0,0 +1,91 @@
1
+ <script type="text/javascript">
2
+ let udyaconfig={};
3
+ RED.nodes.registerType('Alice-Get',{
4
+ category: 'alice',
5
+ inputs:1,
6
+ outputs:1,
7
+ icon: "alice.png",
8
+ color: "#D8BFD8",
9
+ defaults:{
10
+ service: {value:"", type:"alice-service"},
11
+ device: {value:undefined}
12
+ },
13
+ label: function(){
14
+ return this.name || "Alice-Get";
15
+ },
16
+ oneditprepare: ()=>{
17
+ $("#node-input-service").change(function(e){
18
+ if (this.value &&this.value!='_ADD_'){
19
+ $.ajax({
20
+ url: "/noderedhome/"+this.value+"/getfullconfig",
21
+ type:"GET"
22
+ })
23
+ .done(result=>{
24
+ // RED.notify("Full Alice SmartHome config successfully retrieved", {type:"success"});
25
+ console.log(result);
26
+ udyaconfig=result;
27
+ updateHouse(result.households);
28
+ })
29
+ .fail(error=>{
30
+ RED.notify("Error when retrieve Alice SmartHome config", {type:"error"});
31
+ });
32
+ }
33
+ });
34
+ $('#node-input-home')
35
+ .prop('disabled', 'disabled')
36
+ .change((e)=>{
37
+ let val = $('#node-input-home').find(":selected").val();
38
+ console.log(val);
39
+ });
40
+
41
+ $('#node-input-room').prop('disabled', 'disabled');
42
+ $('#node-input-device').prop('disabled', 'disabled');
43
+
44
+ }
45
+ });
46
+ function updateHouse(data){
47
+ $('#node-input-home')
48
+ .find('option')
49
+ .remove()
50
+ .end();
51
+ udyaconfig.households.forEach(h => {
52
+ $('#node-input-home')
53
+ .append('<option value="'+h.id+'">'+h.name+'</option>');
54
+ });
55
+ $('#node-input-home')
56
+ .prop('disabled', false);
57
+ };
58
+ function updaterooms(house){
59
+ $('#node-input-room')
60
+ .find('option')
61
+ .remove()
62
+ .end();
63
+ udyaconfig.households.forEach(h => {
64
+ if (h.household_id==house){
65
+ $('#node-input-room')
66
+ .append('<option value="'+h.id+'">'+h.name+'</option>');
67
+ }
68
+ });
69
+ $('#node-input-room').prop('disabled', false);
70
+ };
71
+
72
+ </script>
73
+
74
+ <script type="text/x-red" data-template-name="Alice-Get">
75
+ <div class="form-row">
76
+ <label for="node-input-service">Account</label>
77
+ <input id="node-input-service">
78
+ </div>
79
+ <div class="form-row">
80
+ <label for="node-input-home">Home</label>
81
+ <select id="node-input-home" style="width: 70%;"></select>
82
+ </div>
83
+ <div class="form-row">
84
+ <label for="node-input-room">Room</label>
85
+ <select id="node-input-room" style="width: 70%;"></select>
86
+ </div>
87
+ <div class="form-row">
88
+ <label for="node-input-device">Room</label>
89
+ <select id="node-input-device" style="width: 70%;"></select>
90
+ </div>
91
+ </script>
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ ;
3
+ module.exports = (RED) => {
4
+ function AliceGet(config) {
5
+ RED.nodes.createNode(this, config);
6
+ const service = RED.nodes.getNode(config.service);
7
+ }
8
+ ;
9
+ RED.nodes.registerType("Alice-Get", AliceGet);
10
+ };
@@ -214,8 +214,6 @@
214
214
  .find('option')
215
215
  .remove()
216
216
  .end()
217
- // .append('<option value="unit.gigacalorie">Gcal</option>')
218
- // .val('unit.gigacalorie')
219
217
  .prop('disabled', 'disabled');
220
218
  break;
221
219
  default:
@@ -64,7 +64,7 @@ function AliceSensor(config){
64
64
  if (done) {done();}
65
65
  return;
66
66
  };
67
- if (unit == 'unit.temperature.celsius'){
67
+ if (unit == 'unit.temperature.celsius' || unit == 'unit.ampere'){
68
68
  msg.payload = +msg.payload.toFixed(1);
69
69
  }else {
70
70
  msg.payload = +msg.payload.toFixed(0);
package/nodes/alice.js CHANGED
@@ -1,125 +1,137 @@
1
- const mqtt = require('mqtt');
2
- const axios = require('axios');
3
-
4
- module.exports = function(RED) {
5
- //Sevice node, Alice-Service (credential)
6
- function AliceService(config) {
7
- RED.nodes.createNode(this,config);
8
- this.debug("Starting Alice service...");
9
-
10
- const email = this.credentials.email;
11
- const login = this.credentials.id;
12
- const password = this.credentials.password;
13
- const token = this.credentials.token;
14
-
15
- const suburl = Buffer.from(email).toString('base64');
16
- RED.httpAdmin.get("/noderedhome/"+suburl+"/clearalldevice",(req,res)=>{
17
- const option = {
18
- method: 'POST',
19
- url: 'https://api.nodered-home.ru/gtw/device/clearallconfigs',
20
- headers: {
21
- 'content-type': 'application/json',
22
- 'Authorization': "Bearer "+this.getToken()
23
- },
24
- data: {}
25
- };
26
- axios.request(option)
27
- .then(result=>{
28
- this.trace("All devices configs deleted on gateway successfully");
29
- // console.log(result)
30
- res.sendStatus(200);
31
- })
32
- .catch(error=>{
33
- this.debug("Error when delete All devices configs deleted on gateway: "+error.message);
34
- res.sendStatus(500);
35
- });
36
- });
37
-
38
- this.isOnline = false;
39
- if (!token){
40
- this.error("Authentication is required!!!");
41
- return;
42
- };
43
- const mqttClient = mqtt.connect("mqtts://mqtt.cloud.yandex.net",{
44
- port: 8883,
45
- clientId: login,
46
- rejectUnauthorized: false,
47
- username: login,
48
- password: password,
49
- reconnectPeriod: 10000
50
- });
51
- mqttClient.on("message",(topic, payload)=>{
52
- const arrTopic = topic.split('/');
53
- const data = JSON.parse(payload);
54
- this.trace("Incoming:" + topic +" timestamp:"+new Date().getTime());
55
- if (payload.length && typeof data === 'object'){
56
- if (arrTopic[3]=='message'){
57
- this.warn(data.text);
58
- }else{
59
- this.emit(arrTopic[3],data);
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ const axios_1 = __importDefault(require("axios"));
6
+ const mqtt_1 = __importDefault(require("mqtt"));
7
+ ;
8
+ ;
9
+ ;
10
+ module.exports = (RED) => {
11
+ function AliceService(config) {
12
+ RED.nodes.createNode(this, config);
13
+ this.debug("Starting Alice service... ID: " + this.id);
14
+ const email = this.credentials.email;
15
+ const login = this.credentials.id;
16
+ const password = this.credentials.password;
17
+ const token = this.credentials.token;
18
+ const suburl = Buffer.from(email).toString('base64');
19
+ RED.httpAdmin.get("/noderedhome/" + suburl + "/clearalldevice", (req, res) => {
20
+ const option = {
21
+ method: 'POST',
22
+ url: 'https://api.nodered-home.ru/gtw/device/clearallconfigs',
23
+ headers: {
24
+ 'content-type': 'application/json',
25
+ 'Authorization': "Bearer " + this.getToken()
26
+ },
27
+ data: {}
28
+ };
29
+ axios_1.default.request(option)
30
+ .then(result => {
31
+ this.trace("All devices configs deleted on gateway successfully");
32
+ res.sendStatus(200);
33
+ })
34
+ .catch(error => {
35
+ this.debug("Error when delete All devices configs deleted on gateway: " + error.message);
36
+ res.sendStatus(500);
37
+ });
38
+ });
39
+ RED.httpAdmin.get("/noderedhome/" + this.id + "/getfullconfig", (req, res) => {
40
+ const option = {
41
+ method: 'GET',
42
+ url: 'https://api.iot.yandex.net/v1.0/user/info',
43
+ headers: {
44
+ 'content-type': 'application/json',
45
+ 'Authorization': "Bearer " + this.getToken()
46
+ }
47
+ };
48
+ axios_1.default.request(option)
49
+ .then(result => {
50
+ this.trace("Full Alice SmartHome config successfully retrieved");
51
+ res.json(result.data);
52
+ })
53
+ .catch(error => {
54
+ this.debug("Error when retrieve Alice SmartHome config: " + error.message);
55
+ res.sendStatus(500);
56
+ });
57
+ });
58
+ this.isOnline = false;
59
+ if (!token) {
60
+ this.error("Authentication is required!!!");
61
+ return;
62
+ }
63
+ ;
64
+ const mqttClient = mqtt_1.default.connect("mqtts://mqtt.cloud.yandex.net", {
65
+ port: 8883,
66
+ clientId: login,
67
+ rejectUnauthorized: false,
68
+ username: login,
69
+ password: password,
70
+ reconnectPeriod: 10000
71
+ });
72
+ mqttClient.on("message", (topic, payload) => {
73
+ const arrTopic = topic.split('/');
74
+ const data = JSON.parse(payload);
75
+ this.trace("Incoming:" + topic + " timestamp:" + new Date().getTime());
76
+ if (payload.length && typeof data === 'object') {
77
+ if (arrTopic[3] == 'message') {
78
+ this.warn(data.text);
79
+ }
80
+ else {
81
+ this.emit(arrTopic[3], data);
82
+ }
83
+ ;
84
+ }
85
+ });
86
+ mqttClient.on("connect", () => {
87
+ this.debug("Yandex IOT client connected. ");
88
+ this.emit('online');
89
+ mqttClient.subscribe("$me/device/commands/+", _ => {
90
+ this.debug("Yandex IOT client subscribed to the command");
91
+ });
92
+ });
93
+ mqttClient.on("offline", () => {
94
+ this.debug("Yandex IOT client offline. ");
95
+ this.emit('offline');
96
+ });
97
+ mqttClient.on("disconnect", () => {
98
+ this.debug("Yandex IOT client disconnect.");
99
+ this.emit('offline');
100
+ });
101
+ mqttClient.on("reconnect", () => {
102
+ this.debug("Yandex IOT client reconnecting ...");
103
+ });
104
+ mqttClient.on("error", (err) => {
105
+ this.error("Yandex IOT client Error: " + err.message);
106
+ this.emit('offline');
107
+ });
108
+ this.on('offline', () => {
109
+ this.isOnline = false;
110
+ });
111
+ this.on('online', () => {
112
+ this.isOnline = true;
113
+ });
114
+ this.on('close', (done) => {
115
+ this.emit('offline');
116
+ setTimeout(() => {
117
+ mqttClient.end(false, done);
118
+ }, 500);
119
+ });
120
+ this.send2gate = (path, data, retain) => {
121
+ this.trace("Outgoing: " + path);
122
+ mqttClient.publish(path, data, { qos: 0, retain: retain });
123
+ };
124
+ this.getToken = () => {
125
+ return JSON.parse(token).access_token;
60
126
  };
61
- }
62
- });
63
- mqttClient.on("connect",()=>{
64
- this.debug("Yandex IOT client connected. ");
65
- this.emit('online');
66
- // Подписываемся на получение комманд
67
- mqttClient.subscribe("$me/device/commands/+",_=>{
68
- this.debug("Yandex IOT client subscribed to the command");
69
- });
70
- });
71
- mqttClient.on("offline",()=>{
72
- this.debug("Yandex IOT client offline. ");
73
- this.emit('offline');
74
- });
75
- mqttClient.on("disconnect",()=>{
76
- this.debug("Yandex IOT client disconnect.");
77
- this.emit('offline');
78
- });
79
- mqttClient.on("reconnect",(err)=>{
80
- this.debug("Yandex IOT client reconnecting ...");
81
- });
82
- mqttClient.on("error",(err)=>{
83
- this.error("Yandex IOT client Error: "+ err.message);
84
- this.emit('offline');
85
- });
86
-
87
- this.on('offline', ()=>{
88
- this.isOnline = false;
89
- })
90
-
91
- this.on('online', ()=>{
92
- this.isOnline = true;
93
- })
94
-
95
- this.on('close',(done)=>{
96
- this.emit('offline');
97
- setTimeout(()=>{
98
- mqttClient.end(false,done);
99
- },500)
100
- });
101
-
102
- this.send2gate= (path,data,retain)=>{
103
- // this.debug(path);
104
- // this.debug(data);
105
- this.trace("Outgoing: "+path);
106
- mqttClient.publish(path, data ,{ qos: 0, retain: retain });
107
- }
108
-
109
- this.getToken = ()=>{
110
- return JSON.parse(token).access_token;
111
- }
112
-
113
- };
114
- RED.nodes.registerType("alice-service",AliceService,{
115
- credentials: {
116
- email: {type: "text"},
117
- password: {type: "password"},
118
- token: {type: "password"},
119
- id:{type:"text"}
120
127
  }
121
- });
128
+ ;
129
+ RED.nodes.registerType("alice-service", AliceService, {
130
+ credentials: {
131
+ email: { type: "text" },
132
+ password: { type: "password" },
133
+ token: { type: "password" },
134
+ id: { type: "text" }
135
+ }
136
+ });
122
137
  };
123
-
124
-
125
-
package/package.json CHANGED
@@ -1,13 +1,11 @@
1
1
  {
2
2
  "name": "node-red-contrib-alice",
3
- "version": "2.2.4",
3
+ "version": "2.2.5",
4
4
  "description": "",
5
- "main": "alice.js",
6
5
  "scripts": {
7
6
  "start": "npm run build && node-red",
8
7
  "build": "tsc && npm run copy-html",
9
- "copy-html": "cp ./src/*.html ./nodes/",
10
- "test": "echo \"Error: no test specified\" && exit 1"
8
+ "copy-html": "cp ./src/*.html ./nodes/"
11
9
  },
12
10
  "repository": {
13
11
  "type": "git",
@@ -50,11 +48,32 @@
50
48
  },
51
49
  "devDependencies": {
52
50
  "@types/axios": "^0.14.0",
51
+ "@types/mqtt": "^2.5.0",
53
52
  "@types/node": "^20.11.16",
54
53
  "@types/node-red": "^1.3.4",
55
54
  "@typescript-eslint/eslint-plugin": "^6.20.0",
56
55
  "@typescript-eslint/parser": "^6.20.0",
57
56
  "eslint": "^8.56.0",
57
+ "nodemon": "^3.0.3",
58
58
  "typescript": "^5.3.3"
59
+ },
60
+ "nodemonConfig": {
61
+ "ignoreRoot" : [".git", "test"],
62
+ "restartable": "rs",
63
+ "ignore": [
64
+ "node_modules/**/node_modules",
65
+ "node_modules/**/test",
66
+ "*.log"
67
+ ],
68
+ "verbose": true,
69
+ "delay": "1000",
70
+ "events": {
71
+ "restart": "echo ------ restarted due to: $FILENAME ------"
72
+ },
73
+ "watch": [
74
+ "src/",
75
+ "node_modules/node-red-*"
76
+ ],
77
+ "ext": "js json htm html css"
59
78
  }
60
79
  }
@@ -1,4 +1,5 @@
1
1
  <script type="text/javascript">
2
+ let udyaconfig={};
2
3
  RED.nodes.registerType('Alice-Get',{
3
4
  category: 'alice',
4
5
  inputs:1,
@@ -7,24 +8,84 @@
7
8
  color: "#D8BFD8",
8
9
  defaults:{
9
10
  service: {value:"", type:"alice-service"},
10
- name: {value:null, required: false},
11
+ device: {value:undefined}
11
12
  },
12
13
  label: function(){
13
14
  return this.name || "Alice-Get";
14
15
  },
15
16
  oneditprepare: ()=>{
16
-
17
+ $("#node-input-service").change(function(e){
18
+ if (this.value &&this.value!='_ADD_'){
19
+ $.ajax({
20
+ url: "/noderedhome/"+this.value+"/getfullconfig",
21
+ type:"GET"
22
+ })
23
+ .done(result=>{
24
+ // RED.notify("Full Alice SmartHome config successfully retrieved", {type:"success"});
25
+ console.log(result);
26
+ udyaconfig=result;
27
+ updateHouse(result.households);
28
+ })
29
+ .fail(error=>{
30
+ RED.notify("Error when retrieve Alice SmartHome config", {type:"error"});
31
+ });
32
+ }
33
+ });
34
+ $('#node-input-home')
35
+ .prop('disabled', 'disabled')
36
+ .change((e)=>{
37
+ let val = $('#node-input-home').find(":selected").val();
38
+ console.log(val);
39
+ });
40
+
41
+ $('#node-input-room').prop('disabled', 'disabled');
42
+ $('#node-input-device').prop('disabled', 'disabled');
43
+
17
44
  }
18
- })
45
+ });
46
+ function updateHouse(data){
47
+ $('#node-input-home')
48
+ .find('option')
49
+ .remove()
50
+ .end();
51
+ udyaconfig.households.forEach(h => {
52
+ $('#node-input-home')
53
+ .append('<option value="'+h.id+'">'+h.name+'</option>');
54
+ });
55
+ $('#node-input-home')
56
+ .prop('disabled', false);
57
+ };
58
+ function updaterooms(house){
59
+ $('#node-input-room')
60
+ .find('option')
61
+ .remove()
62
+ .end();
63
+ udyaconfig.households.forEach(h => {
64
+ if (h.household_id==house){
65
+ $('#node-input-room')
66
+ .append('<option value="'+h.id+'">'+h.name+'</option>');
67
+ }
68
+ });
69
+ $('#node-input-room').prop('disabled', false);
70
+ };
71
+
19
72
  </script>
20
73
 
21
74
  <script type="text/x-red" data-template-name="Alice-Get">
22
75
  <div class="form-row">
23
- <label for="node-input-service">Credentials</label>
76
+ <label for="node-input-service">Account</label>
24
77
  <input id="node-input-service">
25
78
  </div>
26
79
  <div class="form-row">
27
- <label for="node-config-input-name">Name</label>
28
- <input type="text" id="node-config-input-name" placeholder="Name">
80
+ <label for="node-input-home">Home</label>
81
+ <select id="node-input-home" style="width: 70%;"></select>
82
+ </div>
83
+ <div class="form-row">
84
+ <label for="node-input-room">Room</label>
85
+ <select id="node-input-room" style="width: 70%;"></select>
86
+ </div>
87
+ <div class="form-row">
88
+ <label for="node-input-device">Room</label>
89
+ <select id="node-input-device" style="width: 70%;"></select>
29
90
  </div>
30
91
  </script>
package/src/alice.html ADDED
@@ -0,0 +1,242 @@
1
+
2
+ <script src="https://widget.cloudpayments.ru/bundles/cloudpayments.js"></script>
3
+ <script type="text/javascript">
4
+ RED.nodes.registerType('alice-service',{
5
+ category: 'config',
6
+ defaults: {
7
+ name: {value:null}
8
+ },
9
+ credentials:{
10
+ email: {type: "text", required:true},
11
+ password: {type: "password", required:true},
12
+ token: {type: "password", required:true},
13
+ id:{type:"text",required:true}
14
+ },
15
+ label: function() {
16
+ return this.name || "Alice-Credentials";
17
+ },
18
+ oneditprepare:function(){
19
+ $('#subscribe-status').text("checking ...");
20
+ let em = $('#node-config-input-email').val();
21
+ let idt = $('#node-config-input-id').val();
22
+ getSubscribeStatus(idt,em,"subscribe-status");
23
+ },
24
+ oneditsave: function(){
25
+ nodename = $('#node-config-input-email').val();
26
+ $('#node-config-input-name').val(nodename);
27
+ }
28
+ });
29
+ function getSubscribeStatus(id, email, contId){
30
+ if (email.length < 3 || id.length < 3){
31
+ return;
32
+ };
33
+ $.ajax({
34
+ url: "https://nodered-home.ru/payment/getsubscribestatus",
35
+ type:"POST",
36
+ headers: {
37
+ 'Content-Type':'application/json'
38
+ },
39
+ crossDomain: true,
40
+ contentType:"application/json",
41
+ data: JSON.stringify({
42
+ id: id,
43
+ email: email
44
+ }),
45
+ dataType: "json",
46
+ format:"json"
47
+ })
48
+ .done(result=>{
49
+ // console.log(result);
50
+ $('#'+contId).text(result.data.text);
51
+ })
52
+ .fail(error=>{
53
+ console.log(error)
54
+ console.error(error.responseJSON);
55
+ RED.notify("Error : "+error.responseJSON.message, {type:"error"});
56
+ });
57
+ };
58
+ function GetToken(code){
59
+ console.log(this.id);
60
+ RED.notify("Request has been sent. Please, wait",{type:"compact"});
61
+ $('#verification_code').prop('disabled', true);
62
+ $('#submit_button').prop('disabled', true);
63
+ $.ajax({
64
+ url: "https://nodered-home.ru/api/v1/getyatoken",
65
+ type:"POST",
66
+ headers: {
67
+ 'Content-Type':'application/json'
68
+ },
69
+ crossDomain: true,
70
+ contentType:"application/json",
71
+ data: JSON.stringify({code:code}),
72
+ dataType: "json",
73
+ format:"json"
74
+ })
75
+ .done(data=>{
76
+ RED.notify("Authentication data, received successfully", {type:"success"});
77
+ $('#node-config-input-email').val(data.email).trigger("change");
78
+ $('#node-config-input-id').val(data.id).trigger("input").trigger("change");
79
+ $('#node-config-input-password').val(data.password).trigger("change");
80
+ $('#node-config-input-token').val(JSON.stringify(data.token)).trigger("change");
81
+ }).fail(error=>{
82
+ console.error(error.responseJSON);
83
+ RED.notify("Error : "+error.responseJSON.message, {type:"error"});
84
+ $('#verification_code').prop('disabled', false);
85
+ $('#submit_button').prop('disabled', false);
86
+ });
87
+ }
88
+ function ClearDevicesConfigOnGateway(email) {
89
+ RED.notify("Request has been sent. Please, wait",{type:"compact"});
90
+ const suburl = btoa(email);
91
+ $.ajax({
92
+ url: "/noderedhome/"+suburl+"/clearalldevice",
93
+ type:"GET"
94
+ })
95
+ .done(result=>{
96
+ RED.notify("All configs have been successfully cleared", {type:"success"});
97
+ })
98
+ .fail(error=>{
99
+ RED.notify("Error when deleting configs on the gateway", {type:"error"});
100
+ })
101
+ };
102
+ function pay(id, email) {
103
+ console.log("Start pay");
104
+ if (!id || !email){
105
+ RED.notify("Please authorize before purchasing a subscription", {type:"error"});
106
+ return;
107
+ };
108
+ $('#subscribe-button').prop('disabled', true);
109
+ RED.notify("Request has been sent. Please, wait",{type:"compact"});
110
+ let paymentWidget = new cp.CloudPayments();
111
+ console.log("Start get pay confi");
112
+ $.ajax({
113
+ url: "https://nodered-home.ru/payment/create",
114
+ type:"POST",
115
+ headers: {
116
+ 'Content-Type':'application/json'
117
+ },
118
+ crossDomain: true,
119
+ contentType:"application/json",
120
+ data: JSON.stringify({
121
+ id: id,
122
+ email: email
123
+ }),
124
+ dataType: "json",
125
+ format:"json"
126
+ })
127
+ .done(paydata=>{
128
+ console.log("pay config done");
129
+ console.log("Start widget");
130
+ paymentWidget.pay('auth', // или 'charge'
131
+ paydata,
132
+ {
133
+ onSuccess: function (result) { // success
134
+ //действие при успешной оплате
135
+ let em = $('#node-config-input-email').val();
136
+ let idt = $('#node-config-input-id').val();
137
+ $('#subscribe-status').text("checking ...");
138
+ setTimeout(() => {
139
+ getSubscribeStatus(idt,em,"subscribe-status");
140
+ }, 2000);
141
+
142
+ },
143
+ onFail: function (reason, options) { // fail
144
+ //действие при неуспешной оплате
145
+ },
146
+ onComplete: function (paymentResult, options) { //Вызывается как только виджет получает от api.cloudpayments ответ с результатом транзакции.
147
+ //например вызов вашей аналитики Facebook Pixel
148
+
149
+ }
150
+ }
151
+ );
152
+ $('#subscribe-button').prop('disabled', false);
153
+ })
154
+ .fail(error=>{
155
+ console.log(error)
156
+ console.error(error.responseJSON);
157
+ RED.notify("Error : "+error.responseJSON.message, {type:"error"});
158
+ $('#subscribe-button').prop('disabled', false);
159
+ })
160
+ };
161
+ </script>
162
+
163
+ <script type="text/x-red" data-template-name="alice-service">
164
+ <input type="hidden" id="node-config-input-name">
165
+ <div name="New Reg">
166
+ <div class="form-row"><b>Credentials</b></div>
167
+ <div class="form-row">
168
+ <p>1. Follow the link and confirm access</p>
169
+ <label>Authentication</label>
170
+ <button
171
+ onclick="window.open('https://oauth.yandex.ru/authorize?response_type=code&client_id=aa882e33283046fc83de54be20a2e5d8')"
172
+ class="ui-button">Yandex Authentication
173
+ </button>
174
+ </div>
175
+
176
+ <div class="form-row">
177
+ <p>2. Input the verification code and click submit</p>
178
+ <label>Code</label>
179
+ <input type="text" id="verification_code" style="width:auto">
180
+ <button id="submit_button" onclick="GetToken($('#verification_code').val())" class="ui-button">Submit</button>
181
+ </div>
182
+ <div class="form-tips" id="node-tip">
183
+ <span>Tip: Data request may take up to 15 seconds, please wait for success or error message.</span>
184
+ </div>
185
+ <hr>
186
+ <div class="form-row">
187
+ <p><b>Authentication result:</b></p>
188
+ </div>
189
+ <div class="form-row">
190
+ <label for="node-config-input-email">Email</label>
191
+ <input type="text" id="node-config-input-email" disabled>
192
+ </div>
193
+ <div class="form-row">
194
+ <label for="node-config-input-id">ID</label>
195
+ <input type="text" id="node-config-input-id" disabled>
196
+ </div>
197
+ <!-- <div class="form-row" style="visibility: hidden;">
198
+ <label for="node-config-input-password">Password</label> -->
199
+ <input type="hidden" id="node-config-input-password" disabled>
200
+ <!-- </div> -->
201
+ <div class="form-row" style="display: none;">
202
+ <label for="node-config-input-token">Token</label>
203
+ <input type="password" id="node-config-input-token" disabled>
204
+ </div>
205
+ </div>
206
+ <hr>
207
+ <div name="subcribtion">
208
+ <div class="form-row">
209
+ <p><b>Subscription:</b></p>
210
+ </div>
211
+ <div class="form-row">
212
+ <label for="node-config-input-token" style="width: 150px">Subscription status:</label> <span id="subscribe-status"></span>
213
+ <div class="form-tips" id="node-tip-subscribe-warn">
214
+ <span>Important:</span><br>
215
+ <span> - By purchasing a subscription, you agree to the terms of the <a href="https://nodered-home.ru/public_offer.pdf" target="_blank">public offer</a></span><br>
216
+ <span> - View and manage your subscriptions <a href="https://my.cloudpayments.ru/ru/unsubscribe" target="_blank">here</a></span>
217
+ <div style="text-align: end">
218
+ <button id="subscribe-button"
219
+ onclick="pay($('#node-config-input-id').val(), $('#node-config-input-email').val())"
220
+ class="ui-button">Buy a subscription
221
+ </button>
222
+ </div>
223
+ </div>
224
+ </div>
225
+ </div>
226
+ <hr>
227
+ <div name="Clearing">
228
+ <div class="form-row"><b>Сlear all data and configs on the gateway</b></div>
229
+ <div class="form-tips" id="node-tip">
230
+ <span>Tip: Сlear all data on the gateway may take up to 15 seconds, please wait for success or error message. When finished, make a full deployment to update the settings on the gateway.</span>
231
+ <div style="text-align: end">
232
+ <button
233
+ style="margin-left: 150px; margin-top: 10px;"
234
+ onclick="ClearDevicesConfigOnGateway($('#node-config-input-email').val())"
235
+ class="ui-button">
236
+ Сlear data on the gateway
237
+ </button>
238
+ </div>
239
+ </div>
240
+ </div>
241
+ </script>
242
+
package/src/alice.ts ADDED
@@ -0,0 +1,296 @@
1
+ import {NodeAPI, Node, NodeDef, NodeCredentials, NodeCredential } from "node-red";
2
+ import axios from "axios";
3
+ import mqtt from "mqtt";
4
+
5
+ interface NodeAliceConfig
6
+ extends NodeDef {
7
+ name:string;
8
+ };
9
+
10
+ interface NodeAliceCredentials
11
+ extends NodeCredential {
12
+ email:string;
13
+ id:string;
14
+ password:string;
15
+ token:string;
16
+ };
17
+
18
+ interface AliceNode
19
+ extends Node {
20
+ credentials: NodeAliceCredentials;
21
+ isOnline:boolean;
22
+ getToken():string;
23
+ send2gate(topic:string,data:any,retain:boolean):void;
24
+ // on(event: 'hello', listener: (name: string) => void): this;
25
+ on(event: string, listener: Function): this;
26
+ };
27
+
28
+ export = (RED: NodeAPI):void =>{
29
+ function AliceService(this:AliceNode, config:NodeAliceConfig):void {
30
+ RED.nodes.createNode(this,config);
31
+ this.debug("Starting Alice service... ID: "+this.id);
32
+
33
+ const email = this.credentials.email;
34
+ const login = this.credentials.id;
35
+ const password = this.credentials.password;
36
+ const token = this.credentials.token;
37
+
38
+ //вызов для удаления всех устройств
39
+ const suburl = Buffer.from(email).toString('base64');
40
+ RED.httpAdmin.get("/noderedhome/"+suburl+"/clearalldevice",(req,res)=>{
41
+ const option = {
42
+ method: 'POST',
43
+ url: 'https://api.nodered-home.ru/gtw/device/clearallconfigs',
44
+ headers: {
45
+ 'content-type': 'application/json',
46
+ 'Authorization': "Bearer "+this.getToken()
47
+ },
48
+ data: {}
49
+ };
50
+ axios.request(option)
51
+ .then(result=>{
52
+ this.trace("All devices configs deleted on gateway successfully");
53
+ // console.log(result)
54
+ res.sendStatus(200);
55
+ })
56
+ .catch(error=>{
57
+ this.debug("Error when delete All devices configs deleted on gateway: "+error.message);
58
+ res.sendStatus(500);
59
+ });
60
+ });
61
+
62
+ RED.httpAdmin.get("/noderedhome/"+this.id+"/getfullconfig",(req,res)=>{
63
+ const option = {
64
+ method: 'GET',
65
+ url: 'https://api.iot.yandex.net/v1.0/user/info',
66
+ headers: {
67
+ 'content-type': 'application/json',
68
+ 'Authorization': "Bearer "+this.getToken()
69
+ }
70
+ };
71
+ axios.request(option)
72
+ .then(result=>{
73
+ this.trace("Full Alice SmartHome config successfully retrieved");
74
+ // console.log(result)
75
+ res.json(result.data);
76
+ })
77
+ .catch(error=>{
78
+ this.debug("Error when retrieve Alice SmartHome config: "+error.message);
79
+ res.sendStatus(500);
80
+ });
81
+ });
82
+
83
+ this.isOnline = false;
84
+ //// проверяем а есть ли токен
85
+ if (!token){
86
+ this.error("Authentication is required!!!");
87
+ return;
88
+ };
89
+ const mqttClient = mqtt.connect("mqtts://mqtt.cloud.yandex.net",{
90
+ port: 8883,
91
+ clientId: login,
92
+ rejectUnauthorized: false,
93
+ username: login,
94
+ password: password,
95
+ reconnectPeriod: 10000
96
+ });
97
+
98
+ mqttClient.on("message",(topic:string, payload:string)=>{
99
+ const arrTopic = topic.split('/');
100
+ const data = JSON.parse(payload);
101
+ this.trace("Incoming:" + topic +" timestamp:"+new Date().getTime());
102
+ if (payload.length && typeof data === 'object'){
103
+ if (arrTopic[3]=='message'){
104
+ this.warn(data.text);
105
+ }else{
106
+ this.emit(arrTopic[3],data);
107
+ };
108
+ }
109
+ });
110
+ mqttClient.on("connect",()=>{
111
+ this.debug("Yandex IOT client connected. ");
112
+ this.emit('online');
113
+ // Подписываемся на получение комманд
114
+ mqttClient.subscribe("$me/device/commands/+",_=>{
115
+ this.debug("Yandex IOT client subscribed to the command");
116
+ });
117
+ });
118
+ mqttClient.on("offline",()=>{
119
+ this.debug("Yandex IOT client offline. ");
120
+ this.emit('offline');
121
+ });
122
+ mqttClient.on("disconnect",()=>{
123
+ this.debug("Yandex IOT client disconnect.");
124
+ this.emit('offline');
125
+ });
126
+ mqttClient.on("reconnect",()=>{
127
+ this.debug("Yandex IOT client reconnecting ...");
128
+ });
129
+ mqttClient.on("error",(err)=>{
130
+ this.error("Yandex IOT client Error: "+ err.message);
131
+ this.emit('offline');
132
+ });
133
+
134
+ this.on('offline', ()=>{
135
+ this.isOnline = false;
136
+ });
137
+
138
+ this.on('online', ()=>{
139
+ this.isOnline = true;
140
+ });
141
+
142
+ this.on('close',(done:Object)=>{
143
+ this.emit('offline');
144
+ setTimeout(()=>{
145
+ mqttClient.end(false,done);
146
+ },500)
147
+ });
148
+
149
+ this.send2gate = (path:string,data:any,retain:boolean)=>{
150
+ // this.debug(path);
151
+ // this.debug(data);
152
+ this.trace("Outgoing: "+path);
153
+ mqttClient.publish(path, data ,{ qos: 0, retain: retain });
154
+ }
155
+
156
+
157
+ this.getToken = ()=>{
158
+ return JSON.parse(token).access_token;
159
+ }
160
+ };
161
+
162
+ RED.nodes.registerType("alice-service",AliceService,{
163
+ credentials: {
164
+ email: {type: "text"},
165
+ password: {type: "password"},
166
+ token: {type: "password"},
167
+ id:{type:"text"}
168
+ }
169
+ });
170
+ }
171
+
172
+ // const mqtt = require('mqtt');
173
+ // const axios = require('axios');
174
+
175
+ // module.exports = function(RED) {
176
+ // //Sevice node, Alice-Service (credential)
177
+ // function AliceService(config) {
178
+ // RED.nodes.createNode(this,config);
179
+ // this.debug("Starting Alice service...");
180
+
181
+ // const email = this.credentials.email;
182
+ // const login = this.credentials.id;
183
+ // const password = this.credentials.password;
184
+ // const token = this.credentials.token;
185
+
186
+ // const suburl = Buffer.from(email).toString('base64');
187
+ // RED.httpAdmin.get("/noderedhome/"+suburl+"/clearalldevice",(req,res)=>{
188
+ // const option = {
189
+ // method: 'POST',
190
+ // url: 'https://api.nodered-home.ru/gtw/device/clearallconfigs',
191
+ // headers: {
192
+ // 'content-type': 'application/json',
193
+ // 'Authorization': "Bearer "+this.getToken()
194
+ // },
195
+ // data: {}
196
+ // };
197
+ // axios.request(option)
198
+ // .then(result=>{
199
+ // this.trace("All devices configs deleted on gateway successfully");
200
+ // // console.log(result)
201
+ // res.sendStatus(200);
202
+ // })
203
+ // .catch(error=>{
204
+ // this.debug("Error when delete All devices configs deleted on gateway: "+error.message);
205
+ // res.sendStatus(500);
206
+ // });
207
+ // });
208
+
209
+ // this.isOnline = false;
210
+ // if (!token){
211
+ // this.error("Authentication is required!!!");
212
+ // return;
213
+ // };
214
+ // const mqttClient = mqtt.connect("mqtts://mqtt.cloud.yandex.net",{
215
+ // port: 8883,
216
+ // clientId: login,
217
+ // rejectUnauthorized: false,
218
+ // username: login,
219
+ // password: password,
220
+ // reconnectPeriod: 10000
221
+ // });
222
+ // mqttClient.on("message",(topic, payload)=>{
223
+ // const arrTopic = topic.split('/');
224
+ // const data = JSON.parse(payload);
225
+ // this.trace("Incoming:" + topic +" timestamp:"+new Date().getTime());
226
+ // if (payload.length && typeof data === 'object'){
227
+ // if (arrTopic[3]=='message'){
228
+ // this.warn(data.text);
229
+ // }else{
230
+ // this.emit(arrTopic[3],data);
231
+ // };
232
+ // }
233
+ // });
234
+ // mqttClient.on("connect",()=>{
235
+ // this.debug("Yandex IOT client connected. ");
236
+ // this.emit('online');
237
+ // // Подписываемся на получение комманд
238
+ // mqttClient.subscribe("$me/device/commands/+",_=>{
239
+ // this.debug("Yandex IOT client subscribed to the command");
240
+ // });
241
+ // });
242
+ // mqttClient.on("offline",()=>{
243
+ // this.debug("Yandex IOT client offline. ");
244
+ // this.emit('offline');
245
+ // });
246
+ // mqttClient.on("disconnect",()=>{
247
+ // this.debug("Yandex IOT client disconnect.");
248
+ // this.emit('offline');
249
+ // });
250
+ // mqttClient.on("reconnect",(err)=>{
251
+ // this.debug("Yandex IOT client reconnecting ...");
252
+ // });
253
+ // mqttClient.on("error",(err)=>{
254
+ // this.error("Yandex IOT client Error: "+ err.message);
255
+ // this.emit('offline');
256
+ // });
257
+
258
+ // this.on('offline', ()=>{
259
+ // this.isOnline = false;
260
+ // })
261
+
262
+ // this.on('online', ()=>{
263
+ // this.isOnline = true;
264
+ // })
265
+
266
+ // this.on('close',(done)=>{
267
+ // this.emit('offline');
268
+ // setTimeout(()=>{
269
+ // mqttClient.end(false,done);
270
+ // },500)
271
+ // });
272
+
273
+ // this.send2gate= (path,data,retain)=>{
274
+ // // this.debug(path);
275
+ // // this.debug(data);
276
+ // this.trace("Outgoing: "+path);
277
+ // mqttClient.publish(path, data ,{ qos: 0, retain: retain });
278
+ // }
279
+
280
+ // this.getToken = ()=>{
281
+ // return JSON.parse(token).access_token;
282
+ // }
283
+
284
+ // };
285
+ // RED.nodes.registerType("alice-service",AliceService,{
286
+ // credentials: {
287
+ // email: {type: "text"},
288
+ // password: {type: "password"},
289
+ // token: {type: "password"},
290
+ // id:{type:"text"}
291
+ // }
292
+ // });
293
+ // };
294
+
295
+
296
+
package/tsconfig.json CHANGED
@@ -56,7 +56,7 @@
56
56
  // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
57
57
  // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
58
58
  "outDir": "./nodes", /* Specify an output folder for all emitted files. */
59
- // "removeComments": true, /* Disable emitting comments. */
59
+ "removeComments": true, /* Disable emitting comments. */
60
60
  // "noEmit": true, /* Disable emitting files from a compilation. */
61
61
  // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
62
62
  // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */