Skip to content

Commit

Permalink
subscriptions and events
Browse files Browse the repository at this point in the history
  • Loading branch information
sammachin committed Dec 27, 2024
1 parent 8ea6f79 commit a40555b
Show file tree
Hide file tree
Showing 13 changed files with 371 additions and 52 deletions.
4 changes: 2 additions & 2 deletions controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ const { Environment, Logger, singleton, StorageService, Time } = require( "@matt
const { BasicInformationCluster, DescriptorCluster, GeneralCommissioning, OnOff } = require( "@matter/main/clusters");
const { ClusterClientObj, ControllerCommissioningFlowOptions } = require("@matter/main/protocol")
const { ManualPairingCodeCodec, QrPairingCodeCodec, NodeId } = require("@matter/main/types")
const os = require('os');

//Some parts of the controller are still in the legacy packages
var { CommissioningController, NodeCommissioningOptions } = require("@project-chip/matter.js")
Expand All @@ -14,6 +13,7 @@ module.exports = function(RED) {
function MatterController(config) {
RED.nodes.createNode(this, config);
var node = this;
node.started = false
node.networkInterface = config.networkInterface
switch (config.logLevel) {
case "FATAL":
Expand Down Expand Up @@ -41,7 +41,7 @@ module.exports = function(RED) {
},
autoConnect: false,
})
node.commissioningController.start();
node.commissioningController.start().then(() => {node.started = true})
}
RED.nodes.registerType("mattercontroller",MatterController);

Expand Down
18 changes: 18 additions & 0 deletions editor_apis.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const {commandOptions, attributeOptions} = require('./utils')
const { BasicInformationCluster } = require( "@matter/main/clusters");
const os = require('os');


module.exports = function(RED) {
Expand Down Expand Up @@ -120,6 +122,22 @@ RED.httpAdmin.get('/_mattercontroller/:cid/device/:did/cluster/:clid/attributes_
}
})

// List Events
RED.httpAdmin.get('/_mattercontroller/:cid/device/:did/cluster/:clid/events', RED.auth.needsPermission('admin.write'), function(req,res){
let ctrl_node = RED.nodes.getNode(req.params.cid)
if (ctrl_node){
ctrl_node.commissioningController.connectNode(BigInt(req.params.did))
.then((conn) => {
let d = conn.getDevices()
events = d[0].getClusterClientById(Number(req.params.clid)).events
res.send(Object.keys(events))
})
}
else {
res.sendStatus(404);
}
})

// Get Attribute options
RED.httpAdmin.get('/_mattermodel/cluster/:clid/attribute/:attr/options', RED.auth.needsPermission('admin.write'), function(req,res){
let data = attributeOptions(req.params.clid, req.params.attr)
Expand Down
93 changes: 93 additions & 0 deletions event.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@

<script type="text/javascript">
RED.nodes.registerType('matterevent',{
category: 'Matter',
color: '#95cc95',
icon: "font-awesome/fa-bell",
inputs:0,
outputs:1,
defaults: {
name: {value:""},
controller: { value: "", type: "mattercontroller", required: true },
device: {value:""},
deviceName: {value:""},
endpoint: {value:""},
cluster: {value:""},
event: {value:""},
topic: {value:""}
},
paletteLabel: "Event",
label: function() {
let defaultName = `${this.deviceName} | ${this.event}`
return this.name || defaultName || "Event";
},
oneditprepare: function() {
if (this.device.length != 0){
setDevice(this.controller, this.device)
}
if (this.cluster.length != 0){
setCluster(this.controller, this.device, this.cluster)
}
if (this.event.length != 0){
setEvent(this.controller, this.device, this.cluster, this.event);
}
$('#refresh-devices').on("click", getDevices);
$('#refresh-clusters').on("click", getClusters);
$('#refresh-event').on("click", getEvents);

},
oneditsave: function() {
let el = document.getElementById('node-input-device');
this.deviceName = el.options[el.selectedIndex].innerHTML;
}
});
</script>

<script type="text/x-red" data-template-name="matterevent">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-controller">Controller</label>
<input type="text" id="node-input-controller" style="width:70%;">
</div>
<div class="form-row">
<label for="node-input-simpleMode">Simple Mode</label>
<input type="checkbox" id="node-input-simpleMode" value="true" >
</div>
<div class="form-row">
<label for="node-input-device">Device</label>
<select type="text" id="node-input-device" style="width:50%;" >
</select>
<a id="refresh-devices" class="btn"><i class="fa fa-refresh"></i></a>
</div>
<!--
<div class="form-row">
<label for="node-input-endpoint">Endpoint</label>
<select type="text" id="node-input-endpoint" style="width:50%;" >
</select>
</div>
-->
<div class="form-row">
<label for="node-input-cluster">Cluster</label>
<select type="text" id="node-input-cluster" style="width:50%;" >
</select>
<a id="refresh-clusters" class="btn"><i class="fa fa-refresh"></i></a>
</div>
<div class="form-row">
<label for="node-input-event">Event</label>
<select type="text" id="node-input-event" style="width:50%;" >
</select>
<a id="refresh-event" class="btn"><i class="fa fa-refresh"></i></a>
</div>
<div class="form-row">
<label for="node-input-topic">Topic</label>
<input type="text" id="node-input-topic">
</div>
</script>

<script type="text/x-red" data-help-name="matterevent">
<p>Receive Events from a Device</p>
</script>

43 changes: 43 additions & 0 deletions event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const {cap} = require('./utils')



module.exports = function(RED) {
function MatterEvent(config) {
RED.nodes.createNode(this, config);
var node = this;
node.controller = RED.nodes.getNode(config.controller);
node.device = BigInt(config.device)
node.endpoint = config.endpoint || 0
node.cluster = Number(config.cluster)
node.event = cap(config.event)
node.topic = config.topic

function subscribe(node){
node.controller.commissioningController.connectNode(node.device)
.then((device) => {
const ep = device.getDevices()
const clc = ep[Number(node.endpoint)].getClusterClientById(Number(node.cluster))
let command = eval(`clc.add${node.event}EventListener`)
command(value => {
msg = {topic: node.topic}
msg.payload = value
node.send(msg)
})
})
}

function waitforserver(node) {
if (!node.controller.started) {
setTimeout(waitforserver, 100, node)
} else {
node.log('Setting Event...')
subscribe(node)
}
}

waitforserver(node)
}
RED.nodes.registerType("matterevent",MatterEvent);

}
4 changes: 2 additions & 2 deletions manager.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
makeTypedInputOpt('Decommission', 'decommissionDevice'),
makeTypedInputOpt('Ping', 'ping'),
makeTypedInputOpt('Open Commisioning Window', 'openCommissioning'),
makeTypedInputOpt('Get', 'getDevice'),
'msg', 'flow', 'global', 'jsonata', 'env'],
makeTypedInputOpt('Get', 'getDevice')
],
typeField: $('#node-input-methodType')
});
}
Expand Down
3 changes: 3 additions & 0 deletions manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,11 @@ module.exports = function(RED) {
msg.payload = nodeId
node.send()
})
.catch((error) => {node.error(error)})
})
.catch((error) => {node.error(error)})
})
.catch((error) => {node.error(error)})
break;
case 'decommissionDevice':
break;
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
"matter-command": "command.js",
"matter-readattr": "read_attribute.js",
"matter-writeattr": "write_attribute.js",
"matter-editorapis": "editor_apis.js"
"matter-editorapis": "editor_apis.js",
"matter-subscribe": "subscribe.js",
"matter-event": "event.js"
}
}
}
4 changes: 2 additions & 2 deletions read_attribute.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@
setCluster(this.controller, this.device, this.cluster)
}
if (this.attr.length != 0){
setAttribute(this.controller, this.device, this.cluster, this.attr);
setAttribute(this.controller, this.device, this.cluster, this.attr, writable=false);
}
$('#refresh-devices').on("click", getDevices);
$('#refresh-clusters').on("click", getClusters);
$('#refresh-attr').on("click", getAttributes);
$('#refresh-attr').on("click", function() { getAttributes(writable=false); } );

},
oneditsave: function() {
Expand Down
55 changes: 13 additions & 42 deletions repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,46 +24,17 @@ await commissioningController.start();
let longDiscriminator = undefined
let shortDiscriminator = undefined

async function commissionDevice(pc){
let re = new RegExp("MT:.*")
let pcData
if (re.test(pc)) {
pcData = QrPairingCodeCodec.decode(pc)[0]
} else {
pcData = ManualPairingCodeCodec.decode(pc);
}
let options = {
commissioning :{
regulatoryLocation: 2
},
discovery: {
identifierData:
{ shortDiscriminator : pcData.shortDiscriminator } ,
discoveryCapabilities: {
ble : false,
},
},
passcode: pcData.passcode,
}
const nodeId = await commissioningController.commissionNode(options);
console.log(`Commissioning successfully done with nodeId ${nodeId}`);
}

function devices(){
ctrl_node = {commissioningController : commissioningController}
deviceList = {}
info = undefined
const nodes = ctrl_node.commissioningController.getCommissionedNodes();
console.log(nodes)
nodes.forEach(nodeId => {
ctrl_node.commissioningController.connectNode(nodeId)
.then((conn) => {
info = conn.getRootClusterClient(BasicInformationCluster)
info.getNodeLabelAttribute().then((nodeLabel) => {
console.log(nodeLabel)
deviceList[nodeId] = nodeLabel
})
})
})
return info
}
info = undefined
var nodes = commissioningController.getCommissionedNodes();
var conn = await commissioningController.connectNode(nodes[1])
var devices = conn.getDevices()
var clc = devices[0].getClusterClientById(69)

clc.addOnOffAttributeListener(value => {
console.log("subscription onOffStatus", value);
})

clc.addStateChangeEventListener (value => {
console.log("subscription StateChange", value);
})
55 changes: 53 additions & 2 deletions resources/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const simpleAttributes = {
'3' : ["identifyTime"],
'6' : ["onOff", "onTime", "offWaitTime", "startUpOnOff"],
'8' : ["currentLevel", "maxLevel", "minLevel", ],
'69' : ["stateValue"],
'768' : ["currentMode", "currentHue", "currentSaturation", "colorTemperatureMireds"]
}

Expand Down Expand Up @@ -209,7 +210,7 @@ function setCommand(ctrl_node, device, cluster, command){
}

function getAttributes(writable=false){
console.log('getAttributes')
console.log(`getAttributes, ${writable}`)
ctrl_node = document.getElementById('node-input-controller').value
device = document.getElementById('node-input-device').value
cluster = document.getElementById('node-input-cluster').value
Expand All @@ -220,6 +221,7 @@ function getAttributes(writable=false){
} else{
url =`_mattercontroller/${ctrl_node}/device/${device}/cluster/${cluster}/attributes`
}
console.log(url)
$.get(url, function(r) {
var attrs = document.getElementById("node-input-attr");
removeOptions(attrs)
Expand Down Expand Up @@ -248,7 +250,7 @@ function getAttributes(writable=false){
}
}
function setAttribute(ctrl_node, device, cluster, attr, writable=false){
console.log(`setAttribute , ${ctrl_node}, ${device}, ${cluster}, ${attr}`)
console.log(`setAttribute , ${ctrl_node}, ${device}, ${cluster}, ${attr}, ${writable}`)
let simpleMode = document.getElementById("node-input-simpleMode")
if ((ctrl_node != '_ADD_' || ctrl_node != undefined) || (device != undefined || device != '__SELECT__') && (cluster != undefined || cluster != '__SELECT__')){
if (writable){
Expand Down Expand Up @@ -287,6 +289,55 @@ function setAttribute(ctrl_node, device, cluster, attr, writable=false){

}

function getEvents(){
console.log('getEvents')
ctrl_node = document.getElementById('node-input-controller').value
device = document.getElementById('node-input-device').value
cluster = document.getElementById('node-input-cluster').value
if ((ctrl_node != '_ADD_' || ctrl_node != undefined) && (device != undefined || device != '__SELECT__') && (cluster != undefined || cluster != '__SELECT__')){
url =`_mattercontroller/${ctrl_node}/device/${device}/cluster/${cluster}/events`
$.get(url, function(r) {
var events = document.getElementById("node-input-event");
removeOptions(events)
var option = document.createElement("option");
option.text = '--SELECT--'
option.value = undefined
events.add(option)
r.forEach(d => {
var option = document.createElement("option");
option.text = d
option.value = d
option.id = d
events.add(option);
});
})
}
}
function setEvent(ctrl_node, device, cluster, event){
console.log(`setEvent , ${ctrl_node}, ${device}, ${cluster}, ${event}`)
if ((ctrl_node != '_ADD_' || ctrl_node != undefined) || (device != undefined || device != '__SELECT__') && (cluster != undefined || cluster != '__SELECT__')){
url =`_mattercontroller/${ctrl_node}/device/${device}/cluster/${cluster}/events`
$.get(url, function(r) {
var events = document.getElementById("node-input-event");
removeOptions(events)
var option = document.createElement("option");
option.text = '--SELECT--'
option.value = undefined
events.add(option)
r.forEach(d => {
var option = document.createElement("option");
option.text = d
option.value = d
option.id = d
if (event == d) { option.selected=true}
events.add(option);
});
})
}

}



function getCommandOpts(){
let clusterID = document.getElementById("node-input-cluster").value
Expand Down
Loading

0 comments on commit a40555b

Please sign in to comment.