Skip to content

Commit 89fe310

Browse files
committed
feat(subscription)!: add support for PRO plan
1 parent f541809 commit 89fe310

6 files changed

Lines changed: 310 additions & 91 deletions

File tree

connection.html

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,12 @@
171171
})
172172
}
173173

174+
function getPlanInfo(accessToken) {
175+
return execGetRequest(getBackendUrl() + '/plan', {
176+
Authorization: accessToken,
177+
})
178+
}
179+
174180
function checkVersion(version) {
175181
return execGetRequest(
176182
`${$(
@@ -213,6 +219,48 @@
213219
}
214220
}
215221

222+
async function displayPlanInfo() {
223+
try {
224+
const accessToken = await ensureValidAccessToken()
225+
const { plan, allowedDeviceCount, availablePlans, subcriptionToken } =
226+
await getPlanInfo(accessToken)
227+
228+
$('.current-plan-name').text(plan.toUpperCase())
229+
$('#allowed-device-count').text(allowedDeviceCount)
230+
231+
if (plan === 'free') {
232+
$('#plan-name').text(availablePlans[0].name)
233+
234+
//availablePlans is an array, but at this point we're handling only the first element!
235+
const availPlan = availablePlans[0]
236+
237+
$.each(availPlan.features, function (i, feature) {
238+
let $li = $('<li>').text(feature).appendTo('#plan-features')
239+
})
240+
241+
$.each(availPlan.priceTags, async function (i, priceTag) {
242+
$('#' + priceTag.name)
243+
.text(priceTag.tag)
244+
.attr(
245+
'href',
246+
getBackendUrl() + '/checkout?token=' + priceTag.checkoutToken
247+
)
248+
.on('click', () => $('#buy-subscription').hide())
249+
})
250+
251+
$('#buy-subscription').show()
252+
} else {
253+
$('#btn-manage-subscription').attr(
254+
'href',
255+
getBackendUrl() + '/subscription?token=' + subcriptionToken
256+
)
257+
$('#manage-subscription').show()
258+
}
259+
} catch (e) {
260+
console.log(e)
261+
}
262+
}
263+
216264
async function displayRegisteredDevices() {
217265
$('#registered-devices').show()
218266

@@ -411,7 +459,8 @@
411459
if (RED.settings.vshConnectionShowSettings) {
412460
$('.hidden').show()
413461
}
414-
displayRegisteredDevices()
462+
await displayPlanInfo()
463+
await displayRegisteredDevices()
415464
}
416465
}
417466

@@ -460,6 +509,8 @@
460509

461510
<style>
462511
#provisioned,
512+
#manage-subscription,
513+
#buy-subscription,
463514
#registered-devices,
464515
#device-list-table,
465516
#loading-failed,
@@ -485,12 +536,32 @@
485536
border: 3px red dashed;
486537
}
487538

539+
.subscription a {
540+
color: white;
541+
background-color: #00691c;
542+
border: 1px solid;
543+
border-radius: 10px;
544+
padding: 8px;
545+
}
546+
547+
.subscription a:hover,
548+
.subscription a:focus {
549+
background-color: #015217;
550+
color: white;
551+
}
552+
488553
#provisioned h1,
489-
#registered-devices h1 {
554+
#registered-devices h1,
555+
.subscription > h1 {
490556
font-size: 1.5em;
491557
margin-top: 30px;
492558
}
493559

560+
.subscription > h2 {
561+
font-size: 1.2em;
562+
margin-top: 20px;
563+
}
564+
494565
#registered-devices table {
495566
border-collapse: collapse;
496567
width: 100%;
@@ -758,8 +829,31 @@ <h1>Connection Details</h1>
758829
</div>
759830
</div>
760831

832+
<div id="manage-subscription" class="subscription">
833+
<h1>Subscription</h1>
834+
<p>Your current plan: <b class="current-plan-name">{CURRENT-PLAN-NAME}</b></p>
835+
<p>
836+
<a id="btn-manage-subscription" href="" target="_blank">manage subscription</a>
837+
</p>
838+
</div>
839+
840+
<div id="buy-subscription" class="subscription">
841+
<h1>Subscription</h1>
842+
<p>Your current plan: <b class="current-plan-name">{CURRENT-PLAN-NAME}</b></p>
843+
<h2>Upgrade to <span id="plan-name">VSH Premium</span>!</h2>
844+
<p>
845+
<ul id="plan-features">
846+
<!-- <li> elements are dynamically put here -->
847+
</ul>
848+
</p>
849+
<p>
850+
Subscribe for <a id="vsh-pro-yearly" href="" target="_blank">XX EUR per year</a> or <a id="vsh-pro-monthly" href="" target="_blank">X.XX EUR per month</a>
851+
</p>
852+
</div>
853+
761854
<div id="registered-devices">
762855
<h1>Registered Devices</h1>
856+
<p>The <span class="current-plan-name">{CURRENT-PLAN-NAME}</span> plan allows up to <span id="allowed-device-count">?</span> devices</p>
763857
<div id="loading">...loading...</div>
764858
<div id="loading-failed">Loading device list failed!</div>
765859
<div id="empty">

connection.js

Lines changed: 91 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ module.exports = function (RED) {
1717

1818
const node = this
1919

20+
node.plan = 'unknown'
21+
22+
RED.httpAdmin.get(
23+
`/vsh-connection/${node.id}`,
24+
RED.auth.needsPermission('vsh-virtual-device.read'),
25+
function (req, res) {
26+
res.json({ plan: node.plan })
27+
}
28+
)
29+
2030
this.logger = config.debug
2131
? (logMessage, variable = undefined, logLevel = 'log') => {
2232
//logLevel: log | warn | error | trace | debug
@@ -90,12 +100,17 @@ module.exports = function (RED) {
90100

91101
this.registerChildNode = function (nodeId, callbacks) {
92102
if (Object.keys(this.childNodes).length >= this.allowedDeviceCount) {
93-
callbacks.setStatus({
94-
shape: 'dot',
95-
fill: 'gray',
96-
text: 'device limit reached! Upgrade your VSH subscription to get more devices!',
97-
})
98-
return
103+
callbacks.setActive(false)
104+
callbacks.setStatus(
105+
{
106+
shape: 'dot',
107+
fill: 'gray',
108+
text: 'device limit reached! Upgrade your VSH subscription to get more devices!',
109+
},
110+
true //force!
111+
)
112+
} else {
113+
callbacks.setActive(true)
99114
}
100115

101116
this.childNodes[nodeId] = callbacks
@@ -125,10 +140,6 @@ module.exports = function (RED) {
125140
}
126141
}
127142

128-
this.isChildNodeRegistered = function (nodeId) {
129-
return this.childNodes[nodeId] !== undefined
130-
}
131-
132143
this.getLocalDevices = function () {
133144
const localDevices = {}
134145

@@ -149,16 +160,20 @@ module.exports = function (RED) {
149160
return result
150161
}
151162

152-
this.execCallbackForOne = function (nodeId, eventName, eventDetails) {
163+
this.execCallbackForOne = function (
164+
nodeId,
165+
eventName,
166+
params,
167+
...moreParams
168+
) {
153169
if (this.childNodes[nodeId][eventName]) {
154-
return this.childNodes[nodeId][eventName](eventDetails)
170+
return this.childNodes[nodeId][eventName](params, ...moreParams)
155171
}
156172
}
157173

158174
this.requestConfig = function () {
159175
this.isRequestConfigCompleted = false
160176
this.refreshChildrenNodeStatus()
161-
162177
this.publish(`vsh/${this.credentials.thingId}/requestConfig`, {
163178
vshVersion: VSH_VERSION,
164179
})
@@ -238,6 +253,7 @@ module.exports = function (RED) {
238253
deviceId,
239254
friendlyName: devices[deviceId]['friendlyName'],
240255
template: devices[deviceId]['template'],
256+
retrievable: devices[deviceId]['retrievable'],
241257
})
242258
}
243259
}
@@ -260,7 +276,9 @@ module.exports = function (RED) {
260276
shadowDevices[deviceId]['template'] !==
261277
localDevices[deviceId]['template'] ||
262278
shadowDevices[deviceId]['friendlyName'] !==
263-
localDevices[deviceId]['friendlyName']
279+
localDevices[deviceId]['friendlyName'] ||
280+
shadowDevices[deviceId]['retrievable'] !==
281+
localDevices[deviceId]['retrievable']
264282
) {
265283
toBeDiscoveredDevices[deviceId] = localDevices[deviceId]
266284
}
@@ -323,6 +341,17 @@ module.exports = function (RED) {
323341
// },
324342
// }
325343

344+
const isActive = this.execCallbackForOne(deviceId, 'isActive')
345+
346+
if (!isActive) {
347+
this.logger(
348+
`ignoring handleReportState for non-active device ID ${deviceId}`,
349+
null,
350+
'warn'
351+
)
352+
return
353+
}
354+
326355
const currentState = this.execCallbackForOne(deviceId, 'getLocalState')
327356

328357
if (!currentState) {
@@ -366,6 +395,16 @@ module.exports = function (RED) {
366395
// payload: {},
367396
// },
368397
// }
398+
const isActive = this.execCallbackForOne(deviceId, 'isActive')
399+
400+
if (!isActive) {
401+
// this.logger(
402+
// `ignoring handleDirectiveFromAlexa for non-active device ID ${deviceId}`,
403+
// null,
404+
// 'warn'
405+
// )
406+
return
407+
}
369408

370409
// get current device state
371410
const oldState = this.execCallbackForOne(deviceId, 'getLocalState')
@@ -439,6 +478,27 @@ module.exports = function (RED) {
439478
})
440479
}
441480

481+
this.handleOverrideConfig = function (message) {
482+
this.publish(`$aws/things/${this.credentials.thingId}/shadow/get`, {})
483+
484+
if (message.msgRateLimiter) {
485+
const config = message.msgRateLimiter
486+
this.rater.overrideConfig(config)
487+
}
488+
if (message.userIdToken) {
489+
this.userIdToken = message.userIdToken
490+
}
491+
if (message.allowedDeviceCount) {
492+
this.allowedDeviceCount = message.allowedDeviceCount
493+
this.disableUnallowedDevices(message.allowedDeviceCount)
494+
}
495+
496+
node.plan = message.plan
497+
498+
this.isRequestConfigCompleted = true
499+
this.refreshChildrenNodeStatus()
500+
}
501+
442502
this.handleRestart = function ({ semverExpr }) {
443503
if (semverExpr && !semver.satisfies(VSH_VERSION, semverExpr)) {
444504
return
@@ -448,6 +508,8 @@ module.exports = function (RED) {
448508

449509
this.disconnect()
450510

511+
this.execCallbackForAll('setActive', true)
512+
451513
setTimeout(() => {
452514
this.connectAndSubscribe()
453515
const requestConfigJob = () => {
@@ -488,21 +550,7 @@ module.exports = function (RED) {
488550
this.handlePing(message)
489551
break
490552
case 'overrideConfig':
491-
this.publish(`$aws/things/${this.credentials.thingId}/shadow/get`, {})
492-
493-
if (message.msgRateLimiter) {
494-
const config = message.msgRateLimiter
495-
this.rater.overrideConfig(config)
496-
}
497-
if (message.userIdToken) {
498-
this.userIdToken = message.userIdToken
499-
}
500-
if (message.allowedDeviceCount) {
501-
this.allowedDeviceCount = message.allowedDeviceCount
502-
this.unrigisterUnallowedDevices(message.allowedDeviceCount)
503-
}
504-
this.isRequestConfigCompleted = true
505-
this.refreshChildrenNodeStatus()
553+
this.handleOverrideConfig(message)
506554
break
507555
case 'restart':
508556
this.handleRestart(message)
@@ -545,18 +593,23 @@ module.exports = function (RED) {
545593
return await response.json()
546594
}
547595

548-
this.unrigisterUnallowedDevices = function (allowedDeviceCount) {
596+
this.disableUnallowedDevices = function (allowedDeviceCount) {
549597
let i = 0
550598

551599
for (const nodeId in this.childNodes) {
552600
i++
553601
if (i > allowedDeviceCount) {
554-
this.execCallbackForOne(nodeId, 'setStatus', {
555-
shape: 'dot',
556-
fill: 'gray',
557-
text: 'device limit reached! Upgrade your VSH subscription to get more devices!',
558-
})
559-
this.unregisterChildNode(nodeId)
602+
this.execCallbackForOne(nodeId, 'setActive', false)
603+
this.execCallbackForOne(
604+
nodeId,
605+
'setStatus',
606+
{
607+
shape: 'dot',
608+
fill: 'gray',
609+
text: 'device limit reached! Upgrade your VSH subscription to get more devices!',
610+
},
611+
true //force
612+
)
560613
}
561614
}
562615
}
@@ -723,6 +776,8 @@ module.exports = function (RED) {
723776
}
724777

725778
this.isSubscribed = false
779+
this.isRequestConfigCompleted = false
780+
this.isError = false
726781
}
727782

728783
this.on('close', async function (removed, done) {

readme.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ I dedicated endless hours to this project and really hope it adds value for you!
107107
- rate the 'virtual smart home' skill on the Alexa skill store
108108
- rate the 'virtual smart home' [Node-RED package](https://flows.nodered.org/node/node-red-contrib-virtual-smart-home)
109109
- mention this package in your next blog post / podcast / YouTube
110-
- [donate](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PJ37WU5S4NZ2E&source=url) a few bucks to help cover the infrastructure costs.
110+
- say thank you by [buying me a coffee](https://paypal.me/cornelius/5).
111111

112112
__THANK YOU!__
113113

@@ -192,7 +192,7 @@ Previously the state of virtual devices (e.g. the brightness of a lamp) was kept
192192
## Terms of Use
193193

194194
This package comes without any warranty. Use it, enjoy it, but all at your own
195-
risk. If you are satisfied with this project, please contribute your share for the backend infrastructure via [donation](https://paypal.me/cornelius/5). Thank you!
195+
risk. If you are satisfied with this project, consider [bying me a coffee](https://paypal.me/cornelius/5). Thank you!
196196

197197
### NOTE
198198

version.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
module.exports = '9.9.9'
1+
module.exports = '2.13.1'

0 commit comments

Comments
 (0)