Node-Red configuration
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ui_control.js 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. const statestore = require('../store/state.js')
  2. const { addConnectionCredentials } = require('../utils/index.js')
  3. module.exports = function (RED) {
  4. function UiControlNode (config) {
  5. const node = this
  6. RED.nodes.createNode(this, config)
  7. // which ui does this widget belong to
  8. const ui = RED.nodes.getNode(config.ui)
  9. function updateStore (all, items, msg, prop, value) {
  10. items.forEach(function (item) {
  11. const i = all[item]
  12. if (i) {
  13. // update the state store for each page
  14. statestore.set(ui, i, msg, prop, value)
  15. } else {
  16. node.error("No item with the name '" + item + "' found", msg)
  17. }
  18. })
  19. }
  20. function emit (msg) {
  21. ui.emit('ui-control:' + node.id, msg, node)
  22. }
  23. const evts = {
  24. onInput: function (msg, send, done) {
  25. const wNode = RED.nodes.getNode(node.id)
  26. // handle the logic here of what to do when input is received
  27. if (typeof msg.payload !== 'object') {
  28. msg.payload = { page: msg.payload }
  29. }
  30. // switch to tab name (or number)
  31. if ('tab' in msg.payload || 'page' in msg.payload) {
  32. let page = msg.payload.page || msg.payload.tab
  33. page = (page === undefined) ? '' : page
  34. if (page === '-1' || page === '+1' || typeof (page) === 'number' || page === '') {
  35. // special case for -1 and +1 to switch to previous/next tab
  36. // number to pick specific index
  37. // "" to refresh the page
  38. // ensure consistency in payload format
  39. msg.payload.page = page
  40. emit(msg)
  41. } else {
  42. let pageFound = false
  43. // check we have a valid tab/page name
  44. RED.nodes.eachNode(function (n) {
  45. if (n.type === 'ui-page') {
  46. if (n.name === page) {
  47. // ensure consistency in payload format
  48. msg.payload.page = page
  49. // send a message to the ui to switch to this tab
  50. emit(msg)
  51. pageFound = true
  52. }
  53. }
  54. })
  55. if (!pageFound) {
  56. node.error("No page with the name '" + page + "' found")
  57. }
  58. }
  59. }
  60. // show/hide or enable/disable tabs
  61. if ('tabs' in msg.payload || 'pages' in msg.payload) {
  62. const pages = msg.payload.pages || msg.payload.tabs
  63. // get a map of page name to page object
  64. const allPages = {}
  65. RED.nodes.eachNode((n) => {
  66. if (n.type === 'ui-page') {
  67. allPages[n.name] = n
  68. }
  69. })
  70. // const pMap = RED.nodes.forEach
  71. if ('show' in pages) {
  72. updateStore(allPages, pages.show, msg, 'visible', true)
  73. }
  74. if ('hide' in pages) {
  75. updateStore(allPages, pages.hide, msg, 'visible', false)
  76. }
  77. if ('enable' in pages) {
  78. updateStore(allPages, pages.enable, msg, 'disabled', false)
  79. }
  80. if ('disable' in pages) {
  81. updateStore(allPages, pages.disable, msg, 'disabled', false)
  82. }
  83. // ensure consistency in payload format
  84. msg.payload.pages = pages
  85. // send to front end in order to action there too
  86. emit(msg)
  87. }
  88. // show or hide ui groups
  89. if ('groups' in msg.payload || 'group' in msg.payload) {
  90. const groups = msg.payload.groups || msg.payload.group
  91. // get a map of group name to group object
  92. const allGroups = {}
  93. RED.nodes.eachNode((n) => {
  94. if (n.type === 'ui-group') {
  95. allGroups[n.name] = n
  96. }
  97. })
  98. if ('show' in groups) {
  99. const gs = groups.show.map((g) => {
  100. const levels = g.split(':')
  101. return levels.length > 1 ? levels[1] : g
  102. })
  103. updateStore(allGroups, gs, msg, 'visible', true)
  104. }
  105. if ('hide' in groups) {
  106. const gh = groups.hide.map((g) => {
  107. const levels = g.split(':')
  108. return levels.length > 1 ? levels[1] : g
  109. })
  110. updateStore(allGroups, gh, msg, 'visible', false)
  111. }
  112. if ('enable' in groups) {
  113. const ge = groups.enable.map((g) => {
  114. const levels = g.split(':')
  115. return levels.length > 1 ? levels[1] : g
  116. })
  117. updateStore(allGroups, ge, msg, 'disabled', false)
  118. }
  119. if ('disable' in groups) {
  120. const gd = groups.disable.map((g) => {
  121. const levels = g.split(':')
  122. return levels.length > 1 ? levels[1] : g
  123. })
  124. updateStore(allGroups, gd, msg, 'disabled', true)
  125. }
  126. // ensure consistency in payload format
  127. msg.payload.groups = groups
  128. emit(msg)
  129. // send specific visible/hidden commands via SocketIO here,
  130. // so all logic stays server-side
  131. wNode.send({ payload: 'input' })
  132. }
  133. if ('url' in msg.payload) {
  134. emit(msg)
  135. }
  136. },
  137. onSocket: {
  138. connection: function (conn) {
  139. if (config.events === 'all' || config.events === 'connect') {
  140. const wNode = RED.nodes.getNode(node.id)
  141. let msg = {
  142. payload: 'connect'
  143. }
  144. msg = addConnectionCredentials(RED, msg, conn, ui)
  145. wNode.send(msg)
  146. }
  147. },
  148. disconnect: function (conn) {
  149. if (config.events === 'all' || config.events === 'connect') {
  150. const wNode = RED.nodes.getNode(node.id)
  151. let msg = {
  152. payload: 'lost'
  153. }
  154. msg = addConnectionCredentials(RED, msg, conn, ui)
  155. wNode.send(msg)
  156. }
  157. },
  158. 'ui-control': function (conn, id, evt, payload) {
  159. if (id === node.id && (config.events === 'all' || config.events === 'change')) {
  160. // this message was sent by this particular node
  161. if (evt === 'change') {
  162. const wNode = RED.nodes.getNode(node.id)
  163. let msg = {
  164. payload: 'change',
  165. tab: payload.page, // index of tab
  166. name: payload.name // page name
  167. }
  168. msg = addConnectionCredentials(RED, msg, conn, ui)
  169. wNode.send(msg)
  170. }
  171. }
  172. }
  173. }
  174. }
  175. // inform the dashboard UI that we are adding this node
  176. ui.register(null, null, node, config, evts)
  177. node.on('close', function (removed, done) {
  178. if (removed) {
  179. // handle node being removed
  180. ui?.deregister(null, null, node)
  181. }
  182. done()
  183. })
  184. }
  185. RED.nodes.registerType('ui-control', UiControlNode)
  186. }