node-red-contrib-room-tools 1.8.2 → 1.8.3-visual-binding.17

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.
package/room-gateway.js DELETED
@@ -1,57 +0,0 @@
1
- const {createGateway,PREFIX}=require('./gateway.cjs')
2
- const fs=require('node:fs')
3
- const path=require('node:path')
4
-
5
- function storedCredentials(RED,nodeId){
6
- const files=[]
7
- const add=file=>{if(file&&!files.includes(file))files.push(file)}
8
- const flowFile=RED.settings.flowFile
9
- const credentialsFile=RED.settings.credentialsFile
10
- if(path.isAbsolute(String(credentialsFile||'')))add(credentialsFile)
11
- if(path.isAbsolute(String(flowFile||''))){
12
- add(path.join(path.dirname(flowFile),path.basename(flowFile,path.extname(flowFile))+'_cred.json'))
13
- if(credentialsFile)add(path.resolve(path.dirname(flowFile),credentialsFile))
14
- }
15
- const projects=path.join(RED.settings.userDir||'', 'projects')
16
- try{
17
- for(const entry of fs.readdirSync(projects,{withFileTypes:true})){
18
- if(!entry.isDirectory())continue
19
- add(path.join(projects,entry.name,'flow_cred.json'))
20
- }
21
- }catch{}
22
- for(const file of files){
23
- try{
24
- const credentials=JSON.parse(fs.readFileSync(file,'utf8'))[nodeId]
25
- if(credentials?.username&&typeof credentials.password==='string')return credentials
26
- }catch{}
27
- }
28
- return null
29
- }
30
-
31
- module.exports=function(RED){
32
- const active=new Map()
33
- RED.httpNode.use((req,res,next)=>{
34
- const route=new URL(req.url,'http://localhost').pathname
35
- const match=route.match(new RegExp('^'+PREFIX+'/regions/([^/]+)/'))
36
- if(!match)return next()
37
- const room=decodeURIComponent(match[1])
38
- const gateway=active.get(room)
39
- if(!gateway)return res.status(503).json({error:'Region gateway not configured'})
40
- gateway.handler(req,res).then(handled=>{if(!handled)res.status(404).json({error:'Not found'})}).catch(next)
41
- })
42
- function Gateway(config){
43
- RED.nodes.createNode(this,config)
44
- const room=config.room||'room-01'
45
- if(!/^[a-zA-Z0-9_-]+$/.test(room)){this.error('Invalid room identifier');return}
46
- if(active.has(room)){this.status({fill:'red',shape:'ring',text:'Duplicate room gateway'});return}
47
- let gateway
48
- const credentials=this.credentials?.username&&this.credentials?.password?this.credentials:storedCredentials(RED,this.id)||{}
49
- try{gateway=createGateway({username:credentials.username,password:credentials.password,room,secureCookie:!!config.secureCookie,onCommand:msg=>this.send(msg)})}
50
- catch(err){this.status({fill:'red',shape:'ring',text:err.message});this.error(err.message);return}
51
- active.set(room,gateway)
52
- this.status({fill:'green',shape:'dot',text:'Access protected: '+(config.room||'room-01')})
53
- this.on('input',(msg,send,done)=>{try{gateway.feedback(msg);if(done)done()}catch(err){if(done)done(err);else this.error(err,msg)}})
54
- this.on('close',()=>{gateway.close();if(active.get(room)===gateway)active.delete(room)})
55
- }
56
- RED.nodes.registerType('room-gateway',Gateway,{credentials:{username:{type:'text'},password:{type:'password'}}})
57
- }
package/room-message.html DELETED
@@ -1,56 +0,0 @@
1
- <script type="text/javascript">
2
- RED.nodes.registerType('room-message', {
3
- category: 'Room Tools',
4
- color: '#a8d5c3',
5
- defaults: {
6
- name: { value: '' },
7
- topic: { value: 'audio/volume', required: true },
8
- value: { value: '65' },
9
- room: { value: 'room-01', required: true },
10
- useInput: { value: false }
11
- },
12
- inputs: 1,
13
- outputs: 1,
14
- icon: 'font-awesome/fa-sliders',
15
- paletteLabel: '面板消息',
16
- label: function () { return this.name || '面板消息 · ' + this.topic },
17
- inputLabels: '触发消息,或使用输入 payload',
18
- outputLabels: 'Template 状态反馈',
19
- oneditprepare: function () {
20
- const hints = {
21
- 'audio/volume': '0 ~ 100', 'lighting/brightness': '0 ~ 100',
22
- 'room/curtain': '0 ~ 100', 'climate/temperature': '16 ~ 30',
23
- 'display/power': 'true / false', 'audio/mute': 'true / false', 'room/meeting': 'true / false',
24
- 'display/source': '无线投屏 / HDMI 1 / HDMI 2 / 视频会议',
25
- 'room/scene': 'meeting / present / video / relax / custom'
26
- }
27
- function update() {
28
- $('#room-message-hint').text(hints[$('#node-input-topic').val()] || '')
29
- $('#node-input-value').prop('disabled', $('#node-input-useInput').prop('checked'))
30
- }
31
- $('#node-input-topic, #node-input-useInput').on('change', update)
32
- update()
33
- }
34
- })
35
- </script>
36
- <script type="text/html" data-template-name="room-message">
37
- <div class="form-row"><label for="node-input-name">名称</label><input id="node-input-name" type="text" placeholder="可选" /></div>
38
- <div class="form-row"><label for="node-input-room">房间 ID</label><input id="node-input-room" type="text" /></div>
39
- <div class="form-row"><label for="node-input-topic">控件</label><select id="node-input-topic" style="width:70%">
40
- <option value="audio/volume">音量</option><option value="audio/mute">静音</option>
41
- <option value="display/power">显示电源</option><option value="display/source">信号源</option>
42
- <option value="lighting/brightness">灯光亮度</option><option value="room/curtain">窗帘开度</option>
43
- <option value="climate/temperature">目标温度</option><option value="room/scene">场景状态</option>
44
- <option value="room/meeting">会议状态</option>
45
- </select></div>
46
- <div class="form-row"><label for="node-input-value">设定值</label><input id="node-input-value" type="text" /></div>
47
- <div class="form-row"><span style="margin-left:100px" id="room-message-hint"></span></div>
48
- <div class="form-row"><label for="node-input-useInput">输入值</label><input id="node-input-useInput" type="checkbox" style="width:auto" /> 使用收到的 msg.payload</div>
49
- </script>
50
- <script type="text/html" data-help-name="room-message">
51
- <p>收到任意输入后,为会议室 Template 生成状态反馈消息。</p>
52
- <p>连接:Inject → 面板消息 → Template。默认发送音量 65。</p>
53
- <p>输出包含 topic、payload、kind: state、room。数值和布尔值自动转换并校验;非法值报错,不输出。</p>
54
- <p>勾选“使用收到的 msg.payload”后,可从上游动态传入值。其他输入属性保留,旧 state 快照移除。</p>
55
- <p>场景状态仅回填场景名称,不执行场景联动;本节点不生成重置命令。</p>
56
- </script>
package/room-message.js DELETED
@@ -1,43 +0,0 @@
1
- module.exports = function (RED) {
2
- const types = {
3
- 'audio/volume': [0, 100], 'lighting/brightness': [0, 100],
4
- 'room/curtain': [0, 100], 'climate/temperature': [16, 30],
5
- 'display/power': 'bool', 'audio/mute': 'bool', 'room/meeting': 'bool',
6
- 'display/source': ['无线投屏', 'HDMI 1', 'HDMI 2', '视频会议'],
7
- 'room/scene': ['meeting', 'present', 'video', 'relax', 'custom']
8
- }
9
- function RoomMessage(config) {
10
- RED.nodes.createNode(this, config)
11
- const node = this
12
- node.on('input', function (msg, send, done) {
13
- send = send || node.send.bind(node)
14
- done = done || function (err) { if (err) node.error(err, msg) }
15
- try {
16
- const rule = types[config.topic]
17
- if (!rule) throw new Error('请选择有效的控件')
18
- const raw = config.useInput ? msg.payload : config.value
19
- let value = raw
20
- if (rule === 'bool') {
21
- if (![true, false, 'true', 'false'].includes(raw)) throw new Error('开关值必须为 true 或 false')
22
- value = raw === true || raw === 'true'
23
- } else if (typeof rule[0] === 'number') {
24
- if (!['number', 'string'].includes(typeof raw) || String(raw).trim() === '') throw new Error('请输入数字')
25
- value = Number(raw)
26
- if (!Number.isFinite(value) || value < rule[0] || value > rule[1]) throw new Error('数值范围应为 ' + rule.join(' ~ '))
27
- } else if (!rule.includes(raw)) throw new Error('可用值: ' + rule.join(', '))
28
- msg.topic = config.topic
29
- msg.payload = value
30
- msg.kind = 'state'
31
- msg.room = config.room || 'room-01'
32
- delete msg.state
33
- node.status({ fill: 'green', shape: 'dot', text: config.topic + ': ' + value })
34
- send(msg)
35
- done()
36
- } catch (err) {
37
- node.status({ fill: 'red', shape: 'ring', text: err.message })
38
- done(err)
39
- }
40
- })
41
- }
42
- RED.nodes.registerType('room-message', RoomMessage)
43
- }