node-red-contrib-alice 2.2.4 → 2.3.1
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.
Potentially problematic release.
This version of node-red-contrib-alice might be problematic. Click here for more details.
- package/.claude/settings.local.json +11 -0
- package/CLAUDE.md +54 -0
- package/nodes/alice-color.js +208 -231
- package/nodes/alice-device.html +6 -1
- package/nodes/alice-device.js +252 -286
- package/nodes/alice-event.js +110 -114
- package/nodes/alice-get.html +91 -0
- package/nodes/alice-get.js +9 -0
- package/nodes/alice-mode.js +136 -145
- package/nodes/alice-onoff.js +126 -130
- package/nodes/alice-range.js +144 -150
- package/nodes/alice-sensor.html +0 -2
- package/nodes/alice-sensor.js +101 -106
- package/nodes/alice-togle.js +118 -125
- package/nodes/alice-video.js +88 -132
- package/nodes/alice.js +127 -122
- package/nodes/types.js +3 -0
- package/package.json +22 -8
- package/src/alice-color.html +255 -0
- package/src/alice-color.ts +227 -0
- package/src/alice-device.html +94 -0
- package/src/alice-device.ts +301 -0
- package/src/alice-event.html +148 -0
- package/src/alice-event.ts +112 -0
- package/src/alice-get.html +67 -6
- package/src/alice-get.ts +12 -15
- package/src/alice-mode.html +296 -0
- package/src/alice-mode.ts +139 -0
- package/src/alice-onoff.html +93 -0
- package/src/alice-onoff.ts +132 -0
- package/src/alice-range.html +293 -0
- package/src/alice-range.ts +144 -0
- package/src/alice-sensor.html +307 -0
- package/src/alice-sensor.ts +103 -0
- package/src/alice-togle.html +96 -0
- package/src/alice-togle.ts +122 -0
- package/src/alice-video.html +90 -0
- package/src/alice-video.ts +99 -0
- package/src/alice.html +242 -0
- package/src/alice.ts +146 -0
- package/src/types.ts +157 -0
- package/tsconfig.json +13 -106
- package/.eslintrc.json +0 -20
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('Video',{
|
|
3
|
+
category: 'alice',
|
|
4
|
+
defaults:{
|
|
5
|
+
device: {value:"", type:"alice-device"},
|
|
6
|
+
name: {value:""},
|
|
7
|
+
protocol: { value:"hls" },
|
|
8
|
+
stream_url:{ value:"", required: true }
|
|
9
|
+
},
|
|
10
|
+
inputs:0,
|
|
11
|
+
outputs:0,
|
|
12
|
+
icon: "alice.png",
|
|
13
|
+
color: "#D8BFD8",
|
|
14
|
+
label: function(){
|
|
15
|
+
return this.name + ":video";
|
|
16
|
+
},
|
|
17
|
+
// oneditprepare: function(){
|
|
18
|
+
// $("#node-input-protocol").typedInput({
|
|
19
|
+
// types: [
|
|
20
|
+
// {
|
|
21
|
+
// value: "protocol",
|
|
22
|
+
// options: [
|
|
23
|
+
// { value: "hls", label: "HLS"},
|
|
24
|
+
// { value: "progressive_mp4", label: "Progressive mp4"},
|
|
25
|
+
// ]
|
|
26
|
+
// }
|
|
27
|
+
// ]
|
|
28
|
+
// });
|
|
29
|
+
// },
|
|
30
|
+
oneditsave: function(){
|
|
31
|
+
deivcename = $('#node-input-device option:selected').text();
|
|
32
|
+
$('#node-input-name').val(deivcename);
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<script type="text/x-red" data-template-name="Video">
|
|
38
|
+
<input type="hidden" id="node-input-name">
|
|
39
|
+
<div class="form-row">
|
|
40
|
+
<label for="node-input-device">Device</label>
|
|
41
|
+
<input id="node-input-device">
|
|
42
|
+
</div>
|
|
43
|
+
<div class="form-row">
|
|
44
|
+
<label for="node-input-protocol">Protocol</label>
|
|
45
|
+
<input type="text" id="node-input-protocol" disabled>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="form-row">
|
|
48
|
+
<label for="node-input-stream_url">URL</label>
|
|
49
|
+
<input type="text" id="node-input-stream_url">
|
|
50
|
+
</div>
|
|
51
|
+
<div class="form-tips" id="node-tip">
|
|
52
|
+
<span>Currently only supports the HLS stream protocol.<br>
|
|
53
|
+
Supported video codecs: H264.<br>
|
|
54
|
+
Maximum video resolution: 1920 x 1080.<br>
|
|
55
|
+
Supported audio codecs: AAC.
|
|
56
|
+
</span>
|
|
57
|
+
</div>
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<script type="text/x-red" data-help-name="Video">
|
|
61
|
+
<p>Getting a video stream from a camera.</p>
|
|
62
|
+
|
|
63
|
+
<h3>Property</h3>
|
|
64
|
+
<dl class="message-properties">
|
|
65
|
+
<dt>Device
|
|
66
|
+
<span class="property-type">Select</span>
|
|
67
|
+
</dt>
|
|
68
|
+
<dd> The device to which this feature is connected </dd>
|
|
69
|
+
<dt>Protocol
|
|
70
|
+
<span class="property-type">disabled</span>
|
|
71
|
+
</dt>
|
|
72
|
+
<dd>Currently only supports the HLS stream protocol.<br>
|
|
73
|
+
Supported video codecs: H264.<br>
|
|
74
|
+
Maximum video resolution: 1920 x 1080.<br>
|
|
75
|
+
Supported audio codecs: AAC.</dd>
|
|
76
|
+
<dt>URL
|
|
77
|
+
<span class="property-type">string</span>
|
|
78
|
+
</dt>
|
|
79
|
+
<dd>Stream URL with parameters.<br>
|
|
80
|
+
The URL must be accessible from the Internet<br>
|
|
81
|
+
In response to a request for a stream using this link, you need to pass headers:<br>
|
|
82
|
+
Content-type: application/vnd.apple.mpegurl<br>
|
|
83
|
+
Access-Control-Allow-Origin: https://yastatic.net</dd>
|
|
84
|
+
</dl>
|
|
85
|
+
|
|
86
|
+
<h3>References</h3>
|
|
87
|
+
<ul>
|
|
88
|
+
<li><a href="https://yandex.ru/dev/dialogs/smart-home/doc/concepts/video_stream.html"> - Yandex documentation</a></li>
|
|
89
|
+
</ul>
|
|
90
|
+
</script>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { NodeAPI, Node } from "node-red";
|
|
2
|
+
import { AliceVideoConfig, AliceDeviceNode, CapabilityState } from "./types.js";
|
|
3
|
+
|
|
4
|
+
export = (RED: NodeAPI): void => {
|
|
5
|
+
function AliceVideo(this: Node, config: AliceVideoConfig): void {
|
|
6
|
+
RED.nodes.createNode(this, config);
|
|
7
|
+
const device = RED.nodes.getNode(config.device) as AliceDeviceNode;
|
|
8
|
+
device.setMaxListeners(device.getMaxListeners() + 1);
|
|
9
|
+
|
|
10
|
+
const id = this.id;
|
|
11
|
+
const ctype = 'devices.capabilities.video_stream';
|
|
12
|
+
const instance = 'get_stream';
|
|
13
|
+
const stream_url = config.stream_url;
|
|
14
|
+
const protocol = config.protocol;
|
|
15
|
+
|
|
16
|
+
const curentState: CapabilityState = {
|
|
17
|
+
type: ctype,
|
|
18
|
+
state: {
|
|
19
|
+
instance: instance,
|
|
20
|
+
value: {
|
|
21
|
+
stream_url: stream_url,
|
|
22
|
+
protocol: protocol
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
this.status({ fill: "red", shape: "dot", text: "offline" });
|
|
28
|
+
|
|
29
|
+
const init = (): void => {
|
|
30
|
+
this.debug("Starting capability initilization ...");
|
|
31
|
+
const capab = {
|
|
32
|
+
type: ctype,
|
|
33
|
+
retrievable: false,
|
|
34
|
+
reportable: false,
|
|
35
|
+
parameters: {
|
|
36
|
+
instance: instance,
|
|
37
|
+
protocols: [protocol]
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
device.setCapability(id, capab)
|
|
42
|
+
.then(() => {
|
|
43
|
+
this.debug("Capability initilization - success!");
|
|
44
|
+
this.status({ fill: "green", shape: "dot", text: "online" });
|
|
45
|
+
})
|
|
46
|
+
.catch(err => {
|
|
47
|
+
this.error("Error on create capability: " + err.message);
|
|
48
|
+
this.status({ fill: "red", shape: "dot", text: "Error" });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
device.updateCapabState(id, curentState)
|
|
52
|
+
.then(() => {
|
|
53
|
+
this.status({ fill: "green", shape: "dot", text: "online" });
|
|
54
|
+
})
|
|
55
|
+
.catch(err => {
|
|
56
|
+
this.error("Error on update capability state: " + err.message);
|
|
57
|
+
this.status({ fill: "red", shape: "dot", text: "Error" });
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
if (device.initState) init();
|
|
62
|
+
|
|
63
|
+
device.on("online", () => {
|
|
64
|
+
init();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
device.on("offline", () => {
|
|
68
|
+
this.status({ fill: "red", shape: "dot", text: "offline" });
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
device.on(id, () => {
|
|
72
|
+
device.updateCapabState(id, curentState)
|
|
73
|
+
.then(() => {
|
|
74
|
+
const str_url = stream_url.slice(0, 25) + "...";
|
|
75
|
+
this.status({ fill: "green", shape: "dot", text: str_url });
|
|
76
|
+
})
|
|
77
|
+
.catch(err => {
|
|
78
|
+
this.error("Error on update capability state: " + err.message);
|
|
79
|
+
this.status({ fill: "red", shape: "dot", text: "Error" });
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
this.on('close', (removed: boolean, done: () => void) => {
|
|
84
|
+
device.setMaxListeners(device.getMaxListeners() - 1);
|
|
85
|
+
if (removed) {
|
|
86
|
+
device.delCapability(id)
|
|
87
|
+
.then(() => { done(); })
|
|
88
|
+
.catch(err => {
|
|
89
|
+
this.error("Error on delete capability: " + err.message);
|
|
90
|
+
done();
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
done();
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
RED.nodes.registerType("Video", AliceVideo);
|
|
99
|
+
};
|
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,146 @@
|
|
|
1
|
+
import { NodeAPI } from "node-red";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
import mqtt from "mqtt";
|
|
4
|
+
import { AliceServiceConfig, AliceServiceNode } from "./types.js";
|
|
5
|
+
|
|
6
|
+
export = (RED: NodeAPI): void => {
|
|
7
|
+
function AliceService(this: AliceServiceNode, config: AliceServiceConfig): void {
|
|
8
|
+
RED.nodes.createNode(this, config);
|
|
9
|
+
this.debug("Starting Alice service... ID: " + this.id);
|
|
10
|
+
|
|
11
|
+
const email = this.credentials.email;
|
|
12
|
+
const login = this.credentials.id;
|
|
13
|
+
const password = this.credentials.password;
|
|
14
|
+
const token = this.credentials.token;
|
|
15
|
+
|
|
16
|
+
//вызов для удаления всех устройств
|
|
17
|
+
const suburl = Buffer.from(email).toString('base64');
|
|
18
|
+
RED.httpAdmin.get("/noderedhome/" + suburl + "/clearalldevice", (_req, res) => {
|
|
19
|
+
axios.request({
|
|
20
|
+
method: 'POST',
|
|
21
|
+
url: 'https://api.nodered-home.ru/gtw/device/clearallconfigs',
|
|
22
|
+
headers: {
|
|
23
|
+
'content-type': 'application/json',
|
|
24
|
+
'Authorization': "Bearer " + this.getToken()
|
|
25
|
+
},
|
|
26
|
+
data: {}
|
|
27
|
+
})
|
|
28
|
+
.then(() => {
|
|
29
|
+
this.trace("All devices configs deleted on gateway successfully");
|
|
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
|
+
RED.httpAdmin.get("/noderedhome/" + this.id + "/getfullconfig", (_req, res) => {
|
|
39
|
+
axios.request({
|
|
40
|
+
method: 'GET',
|
|
41
|
+
url: 'https://api.iot.yandex.net/v1.0/user/info',
|
|
42
|
+
headers: {
|
|
43
|
+
'content-type': 'application/json',
|
|
44
|
+
'Authorization': "Bearer " + this.getToken()
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
.then(result => {
|
|
48
|
+
this.trace("Full Alice SmartHome config successfully retrieved");
|
|
49
|
+
res.json(result.data);
|
|
50
|
+
})
|
|
51
|
+
.catch(error => {
|
|
52
|
+
this.debug("Error when retrieve Alice SmartHome config: " + error.message);
|
|
53
|
+
res.sendStatus(500);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
this.isOnline = false;
|
|
58
|
+
|
|
59
|
+
if (!token) {
|
|
60
|
+
this.error("Authentication is required!!!");
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const mqttClient = mqtt.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
|
+
|
|
73
|
+
mqttClient.on("message", (topic: string, payload: Buffer) => {
|
|
74
|
+
const arrTopic = topic.split('/');
|
|
75
|
+
const data = JSON.parse(payload.toString());
|
|
76
|
+
this.trace("Incoming:" + topic + " timestamp:" + new Date().getTime());
|
|
77
|
+
if (payload.length && typeof data === 'object') {
|
|
78
|
+
if (arrTopic[3] == 'message') {
|
|
79
|
+
this.warn(data.text);
|
|
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
|
+
|
|
94
|
+
mqttClient.on("offline", () => {
|
|
95
|
+
this.debug("Yandex IOT client offline. ");
|
|
96
|
+
this.emit('offline');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
mqttClient.on("disconnect", () => {
|
|
100
|
+
this.debug("Yandex IOT client disconnect.");
|
|
101
|
+
this.emit('offline');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
mqttClient.on("reconnect", () => {
|
|
105
|
+
this.debug("Yandex IOT client reconnecting ...");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
mqttClient.on("error", (err) => {
|
|
109
|
+
this.error("Yandex IOT client Error: " + err.message);
|
|
110
|
+
this.emit('offline');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
this.on('offline', () => {
|
|
114
|
+
this.isOnline = false;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
this.on('online', () => {
|
|
118
|
+
this.isOnline = true;
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
this.on('close', (done: () => void) => {
|
|
122
|
+
this.emit('offline');
|
|
123
|
+
setTimeout(() => {
|
|
124
|
+
mqttClient.end(false, {}, done);
|
|
125
|
+
}, 500);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
this.send2gate = (path: string, data: string, retain: boolean) => {
|
|
129
|
+
this.trace("Outgoing: " + path);
|
|
130
|
+
mqttClient.publish(path, data, { qos: 0, retain: retain });
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
this.getToken = () => {
|
|
134
|
+
return JSON.parse(token).access_token;
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
RED.nodes.registerType("alice-service", AliceService, {
|
|
139
|
+
credentials: {
|
|
140
|
+
email: { type: "text" },
|
|
141
|
+
password: { type: "password" },
|
|
142
|
+
token: { type: "password" },
|
|
143
|
+
id: { type: "text" }
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
};
|