Matter refactor CASE sessions and QRCode (#18196)

This commit is contained in:
s-hadinger 2023-03-14 23:26:53 +01:00 committed by GitHub
parent 4ff9cf4e59
commit 1131ffada1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 11626 additions and 7199 deletions

View File

@ -25,8 +25,6 @@
#include "be_constobj.h"
#include "be_mapping.h"
#include "be_matter_qrcode_min_js.h"
// Matter logo
static const uint8_t MATTER_LOGO[] =
"<svg style='vertical-align:middle;' width='24' height='24' xmlns='http://www.w3.org/2000/svg' viewBox='100 100 240 240'>"
@ -40,6 +38,7 @@ static const uint8_t MATTER_LOGO[] =
extern const bclass be_class_Matter_Counter;
extern const bclass be_class_Matter_Verhoeff;
extern const bclass be_class_Matter_QRCode;
#include "solidify/solidified_Matter_Module.h"
@ -133,6 +132,7 @@ extern const bclass be_class_Matter_TLV; // need to declare it upfront because
#include "solidify/solidified_Matter_TLV.h"
#include "solidify/solidified_Matter_IM_Data.h"
#include "solidify/solidified_Matter_UDPServer.h"
#include "solidify/solidified_Matter_Expirable.h"
#include "solidify/solidified_Matter_Session.h"
#include "solidify/solidified_Matter_Commissioning_Data.h"
#include "solidify/solidified_Matter_Commissioning.h"
@ -177,7 +177,6 @@ static int matter_CD_FFF1_8000(bvm *vm) { return matter_return_static_bytes(vm,
module matter (scope: global, strings: weak) {
_LOGO, comptr(MATTER_LOGO)
_QRCODE_MINJS, comptr(QRCODE_MINJS)
MATTER_OPTION, int(151) // SetOption151 enables Matter
Verhoeff, class(be_class_Matter_Verhoeff)
@ -270,7 +269,12 @@ module matter (scope: global, strings: weak) {
UDPPacket_sent, class(be_class_Matter_UDPPacket_sent)
UDPServer, class(be_class_Matter_UDPServer)
// Expirable
Expirable, class(be_class_Matter_Expirable)
Expirable_list, class(be_class_Matter_Expirable_list)
// Sessions
Fabric, class(be_class_Matter_Fabric)
Session, class(be_class_Matter_Session)
Session_Store, class(be_class_Matter_Session_Store)
@ -291,6 +295,9 @@ module matter (scope: global, strings: weak) {
IM, class(be_class_Matter_IM)
UI, class(be_class_Matter_UI)
// QR Code
QRCode, class(be_class_Matter_QRCode)
// Base38 for QR Code
Base38, class(be_class_Matter_Base38)

View File

@ -0,0 +1,107 @@
/*
be_matter_qrcode.cpp - implements Matter QRCode encoder as UTF8
Copyright (C) 2023 Stephan Hadinger & Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "be_constobj.h"
#include "be_mapping.h"
#include "be_mem.h"
#include "be_exec.h"
#include "qrcodegen.h"
/******************************************************************************************************\
*
*
*
\******************************************************************************************************/
// `matter.QRCode.encode_str(content:string) -> map`
//
int32_t qr_encode_str(bvm *vm) {
int32_t argc = be_top(vm);
if (argc >= 1 && be_isstring(vm, 1)) {
const char * data_str = be_tostring(vm, 1);
size_t data_len = strlen(data_str);
int32_t qr_version = qrcodegen_getMinFitVersion(qrcodegen_Ecc_MEDIUM, data_len);
if (qr_version <= 0) { be_return_nil(vm); }
int32_t qr_size = qrcodegen_version2size(qr_version);
if (qr_size <= 0) { be_return_nil(vm); }
uint8_t * qr0 = (uint8_t *) be_os_malloc(qrcodegen_BUFFER_LEN_FOR_VERSION(qr_version));
if (!qr0) { be_throw(vm, BE_MALLOC_FAIL); }
uint8_t * data_tmp = (uint8_t *) be_os_malloc(qrcodegen_BUFFER_LEN_FOR_VERSION(qr_version));
if (!qr0) { be_os_free(qr0); be_throw(vm, BE_MALLOC_FAIL); }
bool ok = qrcodegen_encodeText(data_str, data_tmp, qr0, qrcodegen_Ecc_MEDIUM, qr_version, qr_version, qrcodegen_Mask_AUTO, true);
if(!ok) {
be_os_free(qr0);
be_os_free(data_tmp);
be_return_nil(vm);
}
qr_size = qrcodegen_getSize(qr0);
size_t len = qr_size * qr_size;
be_newobject(vm, "map");
be_map_insert_int(vm, "size", qr_size);
be_map_insert_int(vm, "version", qr_version);
be_pushstring(vm, "bitmap");
be_newobject(vm, "list");
for (uint32_t i = 0; i < qr_size; i++) {
char line[qr_size];
for (uint32_t j = 0; j < qr_size; j++) {
line[j] = qrcodegen_getModule(qr0, i, j) ? '*' : ' ';
}
be_pushnstring(vm, line, qr_size);
be_data_push(vm, -2);
be_pop(vm, 1);
}
be_pop(vm, 1);
be_data_insert(vm, -3);
be_pop(vm, 2);
be_pop(vm, 1);
be_os_free(qr0);
be_os_free(data_tmp);
be_return(vm);
}
be_raise(vm, "type_error", NULL);
}
#include "be_fixed_be_class_Matter_QRCode.h"
/* @const_object_info_begin
class be_class_Matter_QRCode (scope: global, name: Matter_QRCode, strings: weak) {
encode_str, static_func(qr_encode_str)
// UTF8 basic blocs for QR Codes
// empty, str(" ")
// lowhalf, str("\342\226\204")
// uphalf, str("\342\226\200")
// full, str("\342\226\210")
}
@const_object_info_end */

File diff suppressed because one or more lines are too long

View File

@ -48,10 +48,8 @@ class Matter_Commisioning_Context
var ResponderEph_priv, ResponderEph_pub
var initiatorEph_pub
# Session data
var session_timestamp
var created
var I2RKey, R2IKey, AttestationChallenge
# is commissioning window open
var window_open
def init(responder)
import crypto
@ -59,13 +57,11 @@ class Matter_Commisioning_Context
self.device = responder.device
# generate y once
self.y = crypto.random(32)
self.window_open = true # auto-commissioning for now
end
def process_incoming(msg)
#
if !self.window_open
if !self.device.is_commissioning_open() && msg.opcode >= 0x20 && msg.opcode <= 0x24
tasmota.log("MTR: commissioning not open", 2)
return false
end
@ -93,7 +89,7 @@ class Matter_Commisioning_Context
raise "protocol_error", "invalid PBKDFParamRequest message"
end
var pbkdfparamreq = matter.PBKDFParamRequest().parse(msg.raw, msg.app_payload_idx)
msg.session.set_mode(matter.Session.__PASE)
msg.session.set_mode_PASE()
self.PBKDFParamRequest = msg.raw[msg.app_payload_idx..]
@ -217,7 +213,7 @@ class Matter_Commisioning_Context
if self.cA != self.spake.cA raise "protocol_error", "invalid cA received" end
# send PakeFinished and compute session key
self.session_timestamp = tasmota.rtc()['utc']
self.created = tasmota.rtc()['utc']
var session_keys = crypto.HKDF_SHA256().derive(self.Ke, bytes(), bytes().fromstring(self.SEKeys_Info), 48)
self.I2RKey = session_keys[0..15]
self.R2IKey = session_keys[16..31]
@ -241,19 +237,19 @@ class Matter_Commisioning_Context
var raw = resp.encode(status_raw)
self.responder.send_response(raw, msg.remote_ip, msg.remote_port, nil)
self.responder.add_session(self.future_local_session_id, self.future_initiator_session_id, self.I2RKey, self.R2IKey, self.AttestationChallenge, self.session_timestamp)
self.responder.add_session(self.future_local_session_id, self.future_initiator_session_id, self.I2RKey, self.R2IKey, self.AttestationChallenge, self.created)
end
def find_session_by_destination_id(destinationId, initiatorRandom)
def find_fabric_by_destination_id(destinationId, initiatorRandom)
import crypto
# Validate Sigma1 Destination ID, p.162
# traverse all existing sessions
# traverse all existing fabrics
tasmota.log("MTR: SEARCHING: destinationId=" + destinationId.tohex(), 3)
for session:self.device.sessions.sessions
if session.noc == nil || session.fabric == nil || session.deviceid == nil continue end
for fabric : self.device.sessions.fabrics
if fabric.noc == nil || fabric.fabric_id == nil || fabric.device_id == nil continue end
# compute candidateDestinationId, Section 4.13.2.4.1, “Destination Identifier”
var destinationMessage = initiatorRandom + session.get_ca_pub() + session.get_fabric() + session.get_deviceid()
var key = session.get_ipk_group_key()
var destinationMessage = initiatorRandom + fabric.get_ca_pub() + fabric.fabric_id + fabric.device_id
var key = fabric.get_ipk_group_key()
tasmota.log("MTR: SIGMA1: destinationMessage=" + destinationMessage.tohex(), 3)
tasmota.log("MTR: SIGMA1: key_ipk=" + key.tohex(), 4)
var h = crypto.HMAC_SHA256(key)
@ -261,7 +257,7 @@ class Matter_Commisioning_Context
var candidateDestinationId = h.out()
tasmota.log("MTR: SIGMA1: candidateDestinationId=" + candidateDestinationId.tohex(), 3)
if candidateDestinationId == destinationId
return session
return fabric
end
end
return nil
@ -281,27 +277,29 @@ class Matter_Commisioning_Context
var is_resumption = (sigma1.resumptionID != nil && sigma1.initiatorResumeMIC != nil)
# Check that it's a resumption
var session
var session = msg.session
if is_resumption
session = self.device.sessions.find_session_by_resumption_id(sigma1.resumptionID)
else
session = self.find_session_by_destination_id(sigma1.destinationId, sigma1.initiatorRandom)
# new CASE session, assign to existing fabric
var fabric = self.find_fabric_by_destination_id(sigma1.destinationId, sigma1.initiatorRandom)
session._fabric = fabric
end
if session == nil raise "valuer_error", "StatusReport(GeneralCode: FAILURE, ProtocolId: SECURE_CHANNEL, ProtocolCode: NO_SHARED_TRUST_ROOTS)" end
if session == nil || session._fabric == nil raise "valuer_error", "StatusReport(GeneralCode: FAILURE, ProtocolId: SECURE_CHANNEL, ProtocolCode: NO_SHARED_TRUST_ROOTS)" end
session.source_node_id = msg.source_node_id
session.set_mode(matter.Session.__CASE)
session.set_mode_CASE()
if msg.session
if msg.session != session
self.device.sessions.remove_session(msg.session) # drop the temporary session that was created
end
msg.session = session
session._future_initiator_session_id = sigma1.initiator_session_id # update initiator_session_id
session._future_local_session_id = self.device.sessions.gen_local_session_id()
self.future_local_session_id = session._future_local_session_id
session.__future_initiator_session_id = sigma1.initiator_session_id # update initiator_session_id
session.__future_local_session_id = self.device.sessions.gen_local_session_id()
self.future_local_session_id = session.__future_local_session_id
tasmota.log("MTR: Loc_session=" + str(self.future_local_session_id))
# Check that it's a resumption
if is_resumption && session.shared_secret != nil
if is_resumption
# Resumption p.169
var s1rk_salt = sigma1.initiatorRandom + sigma1.resumptionID
var s1rk_info = bytes().fromstring("Sigma1_Resume")
@ -335,7 +333,7 @@ class Matter_Commisioning_Context
var sigma2resume = matter.Sigma2Resume()
sigma2resume.resumptionID = session.resumption_id
sigma2resume.responderSessionID = session._future_local_session_id
sigma2resume.responderSessionID = session.__future_local_session_id
sigma2resume.sigma2ResumeMIC = Resume2MIC
# # compute session key, p.178
@ -346,7 +344,7 @@ class Matter_Commisioning_Context
var i2r = session_keys[0..15]
var r2i = session_keys[16..31]
var ac = session_keys[32..47]
var session_timestamp = tasmota.rtc()['utc']
var created = tasmota.rtc()['utc']
tasmota.log("MTR: ******************************", 4)
tasmota.log("MTR: I2RKey =" + i2r.tohex(), 4)
@ -355,7 +353,7 @@ class Matter_Commisioning_Context
tasmota.log("MTR: ******************************", 4)
var sigma2resume_raw = sigma2resume.encode()
session._Msg1 = nil
session.__Msg1 = nil
tasmota.log("MTR: sigma2resume_raw: " + sigma2resume_raw.tohex(), 4)
# now package the response message
@ -365,10 +363,7 @@ class Matter_Commisioning_Context
self.responder.send_response(raw, msg.remote_ip, msg.remote_port, resp.message_counter)
# session.close()
session.set_keys(i2r, r2i, ac, session_timestamp)
session.set_persist(true) # keep session on flash
session.set_no_expiration() # never expire
session.save()
session.set_keys(i2r, r2i, ac, created)
return true
else
@ -402,9 +397,9 @@ class Matter_Commisioning_Context
# compute TranscriptHash = Crypto_Hash(message = Msg1)
tasmota.log("****************************************", 4)
session._Msg1 = sigma1.Msg1
tasmota.log("MTR: * MSG1 = " + session._Msg1.tohex(), 4)
var TranscriptHash = crypto.SHA256().update(session._Msg1).out()
session.__Msg1 = sigma1.Msg1
tasmota.log("MTR: * MSG1 = " + session.__Msg1.tohex(), 4)
var TranscriptHash = crypto.SHA256().update(session.__Msg1).out()
# Compute S2K, p.175
var s2k_info = bytes().fromstring(self.S2K_Info)
@ -430,7 +425,7 @@ class Matter_Commisioning_Context
sigma2.encrypted2 = TBEData2Encrypted
tasmota.log("MTR: sigma2: " + matter.inspect(sigma2), 4)
var sigma2_raw = sigma2.encode()
session._Msg2 = sigma2_raw
session.__Msg2 = sigma2_raw
tasmota.log("MTR: sigma2_raw: " + sigma2_raw.tohex(), 4)
# now package the response message
@ -455,10 +450,10 @@ class Matter_Commisioning_Context
tasmota.log("****************************************", 4)
# compute TranscriptHash = Crypto_Hash(message = Msg1 || Msg2)
var TranscriptHash = crypto.SHA256().update(session._Msg1).update(session._Msg2).out()
var TranscriptHash = crypto.SHA256().update(session.__Msg1).update(session.__Msg2).out()
tasmota.log("MTR: * session = " + str(session), 4)
tasmota.log("MTR: session.ipk_epoch_key " + str(session.ipk_epoch_key), 4)
tasmota.log("MTR: session.fabric_compressed " + str(session.fabric_compressed), 4)
tasmota.log("MTR: session.ipk_epoch_key " + str(session.get_ipk_epoch_key()), 4)
tasmota.log("MTR: session.fabric_compressed " + str(session.get_fabric_compressed()), 4)
tasmota.log("MTR: * ipk_group_key = " + session.get_ipk_group_key().tohex(), 4)
tasmota.log("MTR: * TranscriptHash= " + TranscriptHash.tohex(), 4)
@ -466,7 +461,6 @@ class Matter_Commisioning_Context
var s3k = crypto.HKDF_SHA256().derive(session.shared_secret, session.get_ipk_group_key() + TranscriptHash, s3k_info, 16)
tasmota.log("****************************************", 4)
# self.ipk_epoch_key == nil || self.fabric_compressed")
tasmota.log("MTR: * s3k_salt = " + (session.get_ipk_group_key() + TranscriptHash).tohex(), 4)
tasmota.log("MTR: * s3k = " + s3k.tohex(), 4)
tasmota.log("****************************************", 4)
@ -518,10 +512,10 @@ class Matter_Commisioning_Context
# All good, compute new keys
tasmota.log("MTR: Sigma3 verified, computing new keys", 3)
TranscriptHash = crypto.SHA256().update(session._Msg1).update(session._Msg2).update(sigma3.Msg3).out()
# we can now free _Msg1 and _Msg2
session._Msg1 = nil
session._Msg2 = nil
TranscriptHash = crypto.SHA256().update(session.__Msg1).update(session.__Msg2).update(sigma3.Msg3).out()
# we can now free __Msg1 and __Msg2
session.__Msg1 = nil
session.__Msg2 = nil
tasmota.log("MTR: ******************************", 4)
tasmota.log("MTR: shared_secret =" + session.shared_secret.tohex(), 4)
@ -534,7 +528,7 @@ class Matter_Commisioning_Context
var i2r = session_keys[0..15]
var r2i = session_keys[16..31]
var ac = session_keys[32..47]
var session_timestamp = tasmota.rtc()['utc']
var created = tasmota.rtc()['utc']
tasmota.log("MTR: ******************************", 4)
tasmota.log("MTR: I2RKey =" + i2r.tohex(), 4)
@ -555,9 +549,12 @@ class Matter_Commisioning_Context
self.responder.send_response(raw, msg.remote_ip, msg.remote_port, resp.message_counter)
session.close()
session.set_keys(i2r, r2i, ac, session_timestamp)
session.set_keys(i2r, r2i, ac, created)
# CASE Session completed, persist it
session.set_persist(true) # keep session on flash
session.set_no_expiration() # never expire
session.persist_to_fabric()
session.save()
return true

View File

@ -31,6 +31,8 @@ class Matter_Device
var message_handler # `matter.MessageHandler()` object
var sessions # `matter.Session_Store()` objet
var ui
# Commissioning open
var commissioning_open # timestamp for timeout of commissioning (millis()) or `nil` if closed
# information about the device
var commissioning_instance_wifi # random instance name for commissioning
var commissioning_instance_eth # random instance name for commissioning
@ -67,7 +69,7 @@ class Matter_Device
self.commissioning_instance_eth = crypto.random(8).tohex() # 16 characters random hostname
self.sessions = matter.Session_Store()
self.sessions.load()
self.sessions.load_fabrics()
self.message_handler = matter.MessageHandler(self)
self.ui = matter.UI(self)
@ -96,18 +98,35 @@ class Matter_Device
end, self)
end
self.start_basic_commissioning()
self.init_basic_commissioning()
tasmota.add_driver(self)
end
#############################################################
# Start Basic Commissioning Window
def start_basic_commissioning()
def init_basic_commissioning()
# compute PBKDF
self.compute_pbkdf(self.passcode)
# if no fabric is configured, automatically open commissioning at restart
if size(self.sessions.fabrics) == 0
self.start_basic_commissioning()
end
end
#############################################################
# Start Basic Commissioning Window
def start_basic_commissioning()
self.commissioning_open = tasmota.millis() + 5 * 60 * 1000
end
def stop_basic_commissioning()
self.commissioning_open = nil
end
def is_commissioning_open()
return self.commissioning_open != nil
end
def finish_commissioning()
end
@ -176,6 +195,9 @@ class Matter_Device
def every_second()
self.sessions.every_second()
self.message_handler.every_second()
if self.commissioning_open != nil && tasmota.time_reached(self.commissioning_open) # timeout reached, close provisioning
self.commissioning_open = nil
end
end
#############################################################
@ -214,12 +236,12 @@ class Matter_Device
end
#############################################################
# start_operational_dicovery
# start_operational_discovery
#
# Pass control to `device`
def start_operational_dicovery_deferred(session)
def start_operational_discovery_deferred(session)
# defer to next click
tasmota.set_timer(0, /-> self.start_operational_dicovery(session))
tasmota.set_timer(0, /-> self.start_operational_discovery(session))
end
#############################################################
@ -230,7 +252,7 @@ class Matter_Device
#############################################################
# Start Operational Discovery
def start_operational_dicovery(session)
def start_operational_discovery(session)
import crypto
import mdns
import string
@ -241,14 +263,10 @@ class Matter_Device
self.w1 = nil
self.L = nil
# save session as persistant
session.set_no_expiration()
session.set_persist(true)
# close the PASE session, it will be re-opened with a CASE session
session.close()
self.sessions.save()
# we keep the PASE session for 1 minute
session.set_expire_in_seconds(60)
self.mdns_announce_op_discovery(session)
self.mdns_announce_op_discovery(session.get_fabric())
end
#############################################################
@ -611,27 +629,27 @@ class Matter_Device
tasmota.log("MTR: Exception" + str(e) + "|" + str(m), 2)
end
self.mdns_announce_op_discovery_all_sessions()
self.mdns_announce_op_discovery_all_fabrics()
end
#############################################################
# Start UDP mDNS announcements for commissioning for all persisted sessions
def mdns_announce_op_discovery_all_sessions()
for session: self.sessions.sessions
if session.get_deviceid() && session.get_fabric()
self.mdns_announce_op_discovery(session)
def mdns_announce_op_discovery_all_fabrics()
for fabric: self.sessions.fabrics
if fabric.get_device_id() && fabric.get_fabric_id()
self.mdns_announce_op_discovery(fabric)
end
end
end
#############################################################
# Start UDP mDNS announcements for commissioning
def mdns_announce_op_discovery(session)
def mdns_announce_op_discovery(fabric)
import mdns
import string
try
var device_id = session.get_deviceid().copy().reverse()
var k_fabric = session.get_fabric_compressed()
var device_id = fabric.get_device_id().copy().reverse()
var k_fabric = fabric.get_fabric_compressed()
var op_node = k_fabric.tohex() + "-" + device_id.tohex()
tasmota.log("MTR: Operational Discovery node = " + op_node, 2)

View File

@ -0,0 +1,208 @@
#
# Matter_Expirable.be - Support for Matter Expirable and Persistable objects and lists
#
# Copyright (C) 2023 Stephan Hadinger & Theo Arends
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import matter
#@ solidify:Matter_Expirable,weak
#@ solidify:Matter_Expirable_list,weak
#################################################################################
# Matter_Expirable class
#
# Object that can expire after a certain timestamp or
# that does not expire and can be persisted
#
# There are only 3 valid states:
# - not expirable and not persistable
# - expirable and not persistable
# - not expirable and persistable
# - (both expirable and persistable is normally not valid but still supported)
#
#################################################################################
class Matter_Expirable
var _list # the Expirable_list it belongs to (if any)
var _persist # do we persist this sessions or is it remporary (bool)
var _expiration # if not `nil` the entry is removed after this timestamp
#############################################################
def init()
self._persist = false
end
#############################################################
def set_parent_list(l)
self._list = l
end
def get_parent_list()
return self._list
end
#############################################################
def set_persist(p)
self._persist = bool(p)
end
def does_persist()
return self._persist
end
#############################################################
# pre and post process when persist
def persist_pre()
end
def persist_post()
end
#############################################################
# post process after the object was loaded
def hydrate_post()
end
#############################################################
# set absolute time for expiration
def set_no_expiration()
self._expiration = nil
end
#############################################################
# set absolute time for expiration
def set_expire_time(t)
self._expiration = int(t)
end
#############################################################
# set relative time in the future for expiration (in seconds)
def set_expire_in_seconds(s, now)
if s == nil return end
if now == nil now = tasmota.rtc()['utc'] end
self.set_expire_time(now + s)
end
#############################################################
# set relative time in the future for expiration (in seconds)
# returns `true` if expiration date has been reached
def has_expired(now)
if now == nil now = tasmota.rtc()['utc'] end
if self._expiration != nil
return now >= self._expiration
end
return false
end
end
matter.Expirable = Matter_Expirable
#################################################################################
# Matter_Expirable_list class
#
# Subclass of list handling Expirable(s)
#
#################################################################################
class Matter_Expirable_list : list
# no specific attributes
# no specific init()
#############################################################
# Accessors with control of arguments classes
def push(o)
if !isinstance(o, matter.Expirable) raise "type_error", "argument must be of class 'Expirable'" end
o.set_parent_list(self)
return super(self).push(o)
end
def setitem(i, o)
if !isinstance(o, matter.Expirable) raise "type_error", "argument must be of class 'Expirable'" end
o.set_parent_list(self)
return super(self).setitem(i, o)
end
#############################################################
# remove_expired
#
# Check is any object has expired
#
# returns `true` if persistable objects were actually removed (i.e. needs to persist again), `false` instead
def remove_expired()
var dirty = false
var i = 0
while i < size(self)
if self[i].has_expired()
if self[i]._persist dirty = true end # do we need to save
self.remove(i)
else
i += 1
end
end
return dirty
end
#############################################################
# iterator over persistable instances
#
def persistables()
var iterator = self.iter()
var f = def ()
while true
var o = iterator()
if o._persist return o end
end
# ends with an exception
end
return f
end
#############################################################
# every_second
def every_second()
self.remove_expired()
end
end
matter.Expirable_list = Matter_Expirable_list
#-
# tests
a = matter.Expirable()
a.set_persist(true)
b = matter.Expirable()
c = matter.Expirable()
c.set_persist(true)
l = matter.Expirable_list()
l.push(a)
l.push(b)
l.push(c)
print('---')
for e:l print(e) end
print('---')
for e:l.persistables() print(e) end
print('---')
l.every_second()
print(size(l))
l[1].set_expire_time(10)
l.every_second()
print(size(l))
-#

View File

@ -583,8 +583,8 @@ class Matter_IM
ret.suppress_response = (size(fake_read.attributes_requests) == 0) # ret is of class `ReportDataMessage`
ret.subscription_id = sub.subscription_id
self.send_queue.push(matter.IM_ReportDataSubscribed(session.__message_handler, session, ret, sub))
self.send_enqueued(session.__message_handler)
self.send_queue.push(matter.IM_ReportDataSubscribed(session._message_handler, session, ret, sub))
self.send_enqueued(session._message_handler)
end
#############################################################

View File

@ -57,6 +57,7 @@ class Matter_IM_Subscription
var max_interval = req.max_interval_ceiling
if max_interval < 60 max_interval = 60 end
if max_interval > 3600 max_interval = 3600 end
max_interval = 60
self.max_interval = max_interval
self.fabric_filtered = req.fabric_filtered

View File

@ -287,7 +287,7 @@ class Matter_Frame
resp.message_counter = self.session.counter_snd.next()
resp.local_session_id = self.session.initiator_session_id
else
resp.message_counter = self.session._counter_insecure_snd.next()
resp.message_counter = self.session.__counter_insecure_snd.next()
resp.local_session_id = 0
end
@ -320,8 +320,8 @@ class Matter_Frame
resp = matter.Frame(message_handler)
end
resp.remote_ip = session.__ip
resp.remote_port = session.__port
resp.remote_ip = session._ip
resp.remote_port = session._port
resp.flag_dsiz = 0x00
resp.session = session # also copy the session object
@ -330,14 +330,14 @@ class Matter_Frame
resp.message_counter = session.counter_snd.next()
resp.local_session_id = session.initiator_session_id
else
resp.message_counter = session._counter_insecure_snd.next()
resp.message_counter = session.__counter_insecure_snd.next()
resp.local_session_id = 0
end
resp.x_flag_i = 1 # we are the initiator
resp.opcode = opcode
session.__exchange_id += 1 # move to next exchange_id
resp.exchange_id = session.__exchange_id | 0x10000 # special encoding for local exchange_id
session._exchange_id += 1 # move to next exchange_id
resp.exchange_id = session._exchange_id | 0x10000 # special encoding for local exchange_id
resp.protocol_id = 0x0001 # PROTOCOL_ID_INTERACTION_MODEL
resp.x_flag_r = reliable ? 1 : 0
@ -428,8 +428,8 @@ class Matter_Frame
var n = bytes()
n.add(self.flags, 1)
n.add(self.message_counter, 4)
if session.get_mode() == session.__CASE && session.deviceid
n .. session.deviceid
if session.is_CASE() && session.get_device_id()
n .. session.get_device_id()
end
n.resize(13) # add zeros

View File

@ -62,9 +62,9 @@ class Matter_MessageHandler
### unencrypted session, handled by commissioning
var session = self.device.sessions.find_session_source_id_unsecure(frame.source_node_id, 90) # 90 seconds max
tasmota.log("MTR: find session by source_node_id = " + str(frame.source_node_id) + "session_id = " + str(session.local_session_id), 3)
if addr session.__ip = addr end
if port session.__port = port end
session.__message_handler = self
if addr session._ip = addr end
if port session._port = port end
session._message_handler = self
frame.session = session
# check if it's a duplicate
@ -93,9 +93,9 @@ class Matter_MessageHandler
tasmota.log("MTR: frame="+matter.inspect(frame), 3)
return false
end
if addr session.__ip = addr end
if port session.__port = port end
session.__message_handler = self
if addr session._ip = addr end
if port session._port = port end
session._message_handler = self
frame.session = session # keep a pointer of the session in the message
# check if it's a duplicate
@ -174,13 +174,13 @@ class Matter_MessageHandler
end
#############################################################
def add_session(local_session_id, initiator_session_id, i2r, r2i, ac, session_timestamp)
def add_session(local_session_id, initiator_session_id, i2r, r2i, ac, created)
import string
# create session object
tasmota.log(string.format("MTR: add_session local_session_id=%i initiator_session_id=%i", local_session_id, initiator_session_id), 3)
var session = self.device.sessions.create_session(local_session_id, initiator_session_id)
session.set_keys(i2r, r2i, ac, session_timestamp)
session.set_keys(i2r, r2i, ac, created)
end
#############################################################

View File

@ -65,7 +65,7 @@ class Matter_Plugin_Root : Matter_Plugin
if cluster == 0x0030 # ========== GeneralCommissioning cluster 11.9 p.627 ==========
if attribute == 0x0000 # ---------- Breadcrumb ----------
return TLV.create_TLV(TLV.U8, session.__breadcrumb)
return TLV.create_TLV(TLV.U8, session._breadcrumb)
elif attribute == 0x0001 # ---------- BasicCommissioningInfo / BasicCommissioningInfo----------
var bci = TLV.Matter_TLV_struct()
bci.add_TLV(0, TLV.U2, 60) # FailSafeExpiryLengthSeconds
@ -123,9 +123,9 @@ class Matter_Plugin_Root : Matter_Plugin
end
return nwi
elif attribute == 0x0001 # ---------- RebootCount u16 ----------
return TLV.create_TLV(TLV.U2, tasmota.cmd("Status 1")['StatusPRM']['BootCount'])
return TLV.create_TLV(TLV.U2, tasmota.cmd("Status 1", true)['StatusPRM']['BootCount'])
elif attribute == 0x0002 # ---------- UpTime u16 ----------
return TLV.create_TLV(TLV.U4, tasmota.cmd("Status 11")['StatusSTS']['UptimeSec'])
return TLV.create_TLV(TLV.U4, tasmota.cmd("Status 11", true)['StatusSTS']['UptimeSec'])
# TODO add later other attributes
elif attribute == 0x0008 # ---------- TestEventTriggersEnabled bool ----------
return TLV.create_TLV(TLV.BOOL, false) # false - maybe can set to true
@ -155,8 +155,8 @@ class Matter_Plugin_Root : Matter_Plugin
var nocl = TLV.Matter_TLV_array() # NOCs, p.711
for loc_session: self.device.sessions.sessions_active()
var nocs = nocl.add_struct(nil)
nocs.add_TLV(1, TLV.B2, loc_session.noc) # NOC
nocs.add_TLV(2, TLV.B2, loc_session.icac) # ICAC
nocs.add_TLV(1, TLV.B2, loc_session.get_noc()) # NOC
nocs.add_TLV(2, TLV.B2, loc_session.get_icac()) # ICAC
end
return nocl
elif attribute == 0x0001 # ---------- Fabrics / list[FabricDescriptorStruct] ----------
@ -165,14 +165,14 @@ class Matter_Plugin_Root : Matter_Plugin
var root_ca_tlv = TLV.parse(loc_session.get_ca())
var fab = fabrics.add_struct(nil) # encoding see p.303
fab.add_TLV(1, TLV.B2, root_ca_tlv.findsubval(9)) # RootPublicKey
fab.add_TLV(2, TLV.U2, loc_session.admin_vendor) # VendorID
fab.add_TLV(3, TLV.U8, loc_session.fabric) # FabricID
fab.add_TLV(4, TLV.U8, loc_session.deviceid) # NodeID
fab.add_TLV(5, TLV.UTF1, loc_session.fabric_label) # Label
fab.add_TLV(2, TLV.U2, loc_session.get_admin_vendor()) # VendorID
fab.add_TLV(3, TLV.U8, loc_session.get_fabric_compressed()) # FabricID
fab.add_TLV(4, TLV.U8, loc_session.get_device_id()) # NodeID
fab.add_TLV(5, TLV.UTF1, loc_session.get_fabric_label()) # Label
end
return fabrics
elif attribute == 0x0002 # ---------- SupportedFabrics / u1 ----------
return TLV.create_TLV(TLV.U1, 5) # Max 5 fabrics
return TLV.create_TLV(TLV.U1, matter.Fabric._MAX_CASE) # Max 5 fabrics
elif attribute == 0x0003 # ---------- CommissionedFabrics / u1 ----------
var sessions_active = self.device.sessions.sessions_active()
return TLV.create_TLV(TLV.U1, size(sessions_active)) # number of active sessions
@ -199,21 +199,21 @@ class Matter_Plugin_Root : Matter_Plugin
elif attribute == 0x0002 # ---------- VendorID / vendor-id ----------
return TLV.create_TLV(TLV.U2, self.device.vendorid) # Vendor ID reserved for development
elif attribute == 0x0003 # ---------- ProductName / string ----------
return TLV.create_TLV(TLV.UTF1, tasmota.cmd("DeviceName")['DeviceName'])
return TLV.create_TLV(TLV.UTF1, tasmota.cmd("DeviceName", true)['DeviceName'])
elif attribute == 0x0004 # ---------- ProductID / u16 (opt) ----------
return TLV.create_TLV(TLV.U2, 32768) # taken from esp-matter example
elif attribute == 0x0005 # ---------- NodeLabel / string ----------
return TLV.create_TLV(TLV.UTF1, tasmota.cmd("FriendlyName")['FriendlyName1'])
return TLV.create_TLV(TLV.UTF1, tasmota.cmd("FriendlyName", true)['FriendlyName1'])
elif attribute == 0x0006 # ---------- Location / string ----------
return TLV.create_TLV(TLV.UTF1, "XX") # no location
elif attribute == 0x0007 # ---------- HardwareVersion / u16 ----------
return TLV.create_TLV(TLV.U2, 0)
elif attribute == 0x0008 # ---------- HardwareVersionString / string ----------
return TLV.create_TLV(TLV.UTF1, tasmota.cmd("Status 2")['StatusFWR']['Hardware'])
return TLV.create_TLV(TLV.UTF1, tasmota.cmd("Status 2", true)['StatusFWR']['Hardware'])
elif attribute == 0x0009 # ---------- SoftwareVersion / u32 ----------
return TLV.create_TLV(TLV.U2, 1)
elif attribute == 0x000A # ---------- SoftwareVersionString / string ----------
return TLV.create_TLV(TLV.UTF1, tasmota.cmd("Status 2")['StatusFWR']['Version'])
return TLV.create_TLV(TLV.UTF1, tasmota.cmd("Status 2", true)['StatusFWR']['Version'])
elif attribute == 0x0012 # ---------- UniqueID / string 32 max ----------
return TLV.create_TLV(TLV.UTF1, tasmota.wifi().find("mac", ""))
elif attribute == 0x0013 # ---------- CapabilityMinima / CapabilityMinimaStruct ----------
@ -326,7 +326,7 @@ class Matter_Plugin_Root : Matter_Plugin
# 1=DebugText
var ExpiryLengthSeconds = val.findsubval(0, 900)
var Breadcrumb = val.findsubval(1, 0)
session.__breadcrumb = Breadcrumb
session._breadcrumb = Breadcrumb
var afsr = TLV.Matter_TLV_struct()
afsr.add_TLV(0, TLV.U1, 0) # ErrorCode = OK
@ -338,7 +338,7 @@ class Matter_Plugin_Root : Matter_Plugin
var NewRegulatoryConfig = val.findsubval(0) # RegulatoryLocationType Enum
var CountryCode = val.findsubval(1, "XX")
var Breadcrumb = val.findsubval(2, 0)
session.__breadcrumb = Breadcrumb
session._breadcrumb = Breadcrumb
# create SetRegulatoryConfigResponse
# ID=1
# 0=ErrorCode (OK=0)
@ -351,8 +351,10 @@ class Matter_Plugin_Root : Matter_Plugin
elif command == 0x0004 # ---------- CommissioningComplete p.636 ----------
# no data
session.__breadcrumb = 0 # clear breadcrumb
session._breadcrumb = 0 # clear breadcrumb
session.fabric_completed() # fabric information is complete, persist
session.set_no_expiration()
session.save()
# create CommissioningCompleteResponse
# ID=1
@ -456,32 +458,39 @@ class Matter_Plugin_Root : Matter_Plugin
session.set_noc(NOCValue, ICACValue)
session.set_ipk_epoch_key(IpkValue)
session.admin_subject = CaseAdminSubject
session.admin_vendor = AdminVendorId
session.set_admin_subject_vendor(CaseAdminSubject, AdminVendorId)
# extract important information from NOC
var noc_cert = matter.TLV.parse(NOCValue)
var dnlist = noc_cert.findsub(6)
var fabric = dnlist.findsubval(21)
var fabric_id = dnlist.findsubval(21)
var deviceid = dnlist.findsubval(17)
if !fabric || !deviceid
if !fabric_id || !deviceid
tasmota.log("MTR: Error: no fabricid nor deviceid in NOC certificate", 2)
return false
end
# convert fo bytes(8)
if type(fabric) == 'int' fabric = int64(fabric).tobytes() else fabric = fabric.tobytes() end
if type(fabric_id) == 'int' fabric_id = int64(fabric_id).tobytes() else fabric_id = fabric_id.tobytes() end
if type(deviceid) == 'int' deviceid = int64(deviceid).tobytes() else deviceid = deviceid.tobytes() end
var root_ca = matter.TLV.parse(session.get_ca()).findsubval(9) # extract public key from ca
root_ca = root_ca[1..] # remove first byte as per Matter specification
var info = bytes().fromstring("CompressedFabric") # as per spec, 4.3.2.2 p.99
var hk = crypto.HKDF_SHA256()
var fabric_rev = fabric.copy().reverse()
var fabric_rev = fabric_id.copy().reverse()
var k_fabric = hk.derive(root_ca, fabric_rev, info, 8)
session.set_fabric_device(fabric, deviceid, k_fabric)
session.set_fabric_device(fabric_id, deviceid, k_fabric)
# We have a candidate fabric, add it as expirable for 2 minutes
session.persist_to_fabric() # fabric object is completed, persist it
session.fabric_candidate()
# move to next step
self.device.start_operational_dicovery_deferred(session)
self.device.start_operational_discovery_deferred(session)
# session.fabric_completed()
tasmota.log("MTR: ------------------------------------------", 3)
tasmota.log("MTR: fabric=" + matter.inspect(session._fabric), 3)
tasmota.log("MTR: ------------------------------------------", 3)
# create NOCResponse
# 0=StatusCode
# 1=FabricIndex (1-254) (opt)
@ -503,9 +512,9 @@ class Matter_Plugin_Root : Matter_Plugin
var sessions_act = self.device.sessions.sessions_active()
if index >= 1 && index <= size(sessions_act)
var session_deleted = sessions_act[index - 1]
tasmota.log("MTR: removing fabric " + session.fabric.copy().reverse().tohex())
tasmota.log("MTR: removing fabric " + session.get_fabric_id().copy().reverse().tohex())
self.device.sessions.remove_session()
self.device.sessions.save()
self.device.sessions.save_fabrics()
else
# TODO return error 11 InvalidFabricIndex
end
@ -545,7 +554,7 @@ class Matter_Plugin_Root : Matter_Plugin
if attribute == 0x0000 # ---------- Breadcrumb ----------
if type(write_data) == 'int' || isinstance(write_data, int64)
session.__breadcrumb = write_data
session._breadcrumb = write_data
self.attribute_updated(ctx.endpoint, ctx.cluster, ctx.attribute) # TODO should we have a more generalized way each time a write_attribute is triggered, declare the attribute as changed?
return true
else

View File

@ -19,9 +19,196 @@
import matter
#@ solidify:Matter_Fabric,weak
#@ solidify:Matter_Session,weak
#@ solidify:Matter_Session_Store,weak
# for compilation
class Matter_Expirable end
#################################################################################
# Matter_Fabric class
#
# Record all information for a fabric that has provisioned
#
# By convetion:
# attributes with a normal name are persisted (unless they are instances)
# attributes starting with '_' are not persisted
# attributes starting with '__' are cleared when a new session is created
#################################################################################
class Matter_Fabric : Matter_Expirable
static var _MAX_CASE = 5 # maximum number of concurrent CASE sessions per fabric
# Group Key Derivation
static var _GROUP_KEY = "GroupKey v1.0" # starting with double `_` means it's not writable
var _store # reference back to session store
# timestamp
var created
# list of active sessions
var _sessions # only active CASE sessions that need to be persisted
# our own private key
var no_private_key # private key of the device certificate (generated at commissioning)
# NOC information
var root_ca_certificate # root certificate of the initiator
var noc # Node Operational Certificate in TLV Matter Certificate
var icac # Initiator CA Certificate in TLV Matter Certificate
var ipk_epoch_key # timestamp
# Information extracted from `noc`
var fabric_id # fabric identifier as bytes(8) little endian
var fabric_compressed # comrpessed fabric_id identifier, hashed with root_ca public key
var device_id # our own device id bytes(8) little endian
var fabric_label # set by UpdateFabricLabel
# Admin info extracted from NOC/ICAC
var admin_subject
var admin_vendor
#############################################################
def init(store)
import crypto
self._store = store
self._sessions = matter.Expirable_list()
self.fabric_label = ""
self.created = tasmota.rtc()['utc']
end
def get_noc() return self.noc end
def get_icac() return self.icac end
def get_ipk_epoch_key() return self.ipk_epoch_key end
def get_fabric_id() return self.fabric_id end
def get_device_id() return self.device_id end
def get_fabric_compressed() return self.fabric_compressed end
def get_fabric_label() return self.fabric_label end
def get_admin_subject() return self.admin_subject end
def get_admin_vendor() return self.admin_vendor end
#############################################################
# Operational Group Key Derivation, 4.15.2, p.182
def get_ipk_group_key()
if self.ipk_epoch_key == nil || self.fabric_compressed == nil return nil end
import crypto
var hk = crypto.HKDF_SHA256()
var info = bytes().fromstring(self._GROUP_KEY)
var hash = hk.derive(self.ipk_epoch_key, self.fabric_compressed, info, 16)
return hash
end
def get_ca_pub()
var ca = self.root_ca_certificate
if ca
var m = matter.TLV.parse(ca)
return m.findsubval(9)
end
end
#############################################################
# add session to list of persisted sessions
# check for duplicates
def add_session(s)
if self._sessions.find(s) == nil
while size(self._sessions) >= self._MAX_CASE
self._sessions.remove(self._sessions.find(self.get_oldest_session()))
end
self._sessions.push(s)
end
end
def get_oldest_session() return self.get_old_recent_session(true) end
def get_newest_session() return self.get_old_recent_session(false) end
# get the oldest or most recent session (oldest indicates direction)
def get_old_recent_session(oldest)
if size(self._sessions) == 0 return nil end
var session = self._sessions[0]
var timestamp = session.last_used
var idx = 1
while idx < size(self._sessions)
var time2 = self._sessions[idx].last_used
if (oldest ? time2 < timestamp : time2 > timestamp)
session = self._sessions[idx]
timestamp = time2
end
idx += 1
end
return session
end
#############################################################
# Fabric::to_json()
#
# convert a single entry as json
# returns a JSON string
#############################################################
def tojson()
import json
import string
import introspect
var keys = []
for k : introspect.members(self)
var v = introspect.get(self, k)
if type(v) != 'function' && k[0] != '_' keys.push(k) end
end
keys = matter.sort(keys)
var r = []
for k : keys
var v = introspect.get(self, k)
if v == nil continue end
if isinstance(v, bytes) v = "$$" + v.tob64() end # bytes
r.push(string.format("%s:%s", json.dump(str(k)), json.dump(v)))
end
# add sessions
var s = []
for sess : self._sessions.persistables()
s.push(sess.tojson())
end
if size(s) > 0
var s_list = "[" + s.concat(",") + "]"
r.push('"_sessions":' + s_list)
end
return "{" + r.concat(",") + "}"
end
#############################################################
# fromjson()
#
# reads a map and load arguments
# returns an new instance of fabric
# don't load embedded session, this is done by store
# i.e. ignore any key starting with '_'
#############################################################
static def fromjson(store, values)
import string
import introspect
var self = matter.Fabric(store)
for k : values.keys()
if k[0] == '_' continue end # ignore if key starts with '_'
var v = values[k]
# standard values
if type(v) == 'string'
if string.find(v, "0x") == 0 # treat as bytes
introspect.set(self, k, bytes().fromhex(v[2..]))
elif string.find(v, "$$") == 0 # treat as bytes
introspect.set(self, k, bytes().fromb64(v[2..]))
else
introspect.set(self, k, v)
end
else
introspect.set(self, k, v)
end
end
return self
end
end
matter.Fabric = Matter_Fabric
#################################################################################
# Matter_Session class
#
@ -30,31 +217,34 @@ import matter
#
# By convention, names starting with `_` are not persisted
#################################################################################
class Matter_Session
static var __PASE = 1 # PASE authentication in progress
static var __CASE = 2 # CASE authentication in progress
var __store # reference back to session store
class Matter_Session : Matter_Expirable
static var _PASE = 1 # PASE authentication in progress
static var _CASE = 2 # CASE authentication in progress
var _store # reference back to session store
# mode for Session. Can be PASE=1, CASE=2, Established=10 none=0
var mode
# link to a fabric object, temporary and in construction for PASE, persistent for CASE
var _fabric
# sesions
var local_session_id # id for the current local session, starts with 1
var initiator_session_id # id used to respond to the initiator
var session_timestamp # timestamp (UTC) when the session was created
var created # timestamp (UTC) when the session was created
var last_used # timestamp (UTC) when the session was last used
var source_node_id # source node if bytes(8) (opt, used only when session is not established)
# session_ids when the session will be active
var _future_initiator_session_id
var _future_local_session_id
var __future_initiator_session_id
var __future_local_session_id
# counters
var counter_rcv # counter for incoming messages
var counter_snd # counter for outgoing messages
var __exchange_id # exchange id for locally initiated transaction, non-persistent
var _exchange_id # exchange id for locally initiated transaction, non-persistent
# keep track of last known IP/Port of the fabric
var __ip # IP of the last received packet
var __port # port of the last received packet
var __message_handler # pointer to the message handler for this session
var _ip # IP of the last received packet
var _port # port of the last received packet
var _message_handler # pointer to the message handler for this session
# non-session counters
var _counter_insecure_rcv # counter for incoming messages
var _counter_insecure_snd # counter for outgoing messages
var __counter_insecure_rcv # counter for incoming messages
var __counter_insecure_snd # counter for outgoing messages
# encryption keys and challenges
var i2rkey # key initiator to receiver (incoming)
var r2ikey # key receiver to initiator (outgoing)
@ -62,50 +252,68 @@ class Matter_Session
var attestation_challenge # Attestation challenge
var peer_node_id
# breadcrumb
var __breadcrumb # breadcrumb attribute for this session, prefix `__` so that it is not persisted and untouched
# our own private key
var no_private_key # private key of the device certificate (generated at commissioning)
# NOC information
var root_ca_certificate # root certificate of the initiator
var noc # Node Operational Certificate in TLV Matter Certificate
var icac # Initiator CA Certificate in TLV Matter Certificate
var ipk_epoch_key # timestamp
var _breadcrumb # breadcrumb attribute for this session, prefix `__` so that it is not persisted and untouched
# CASE
var resumption_id # bytes(16)
var shared_secret # ECDH shared secret used in CASE
# Information extracted from `noc`
var fabric # fabric identifier as bytes(8) little endian
var fabric_compressed # comrpessed fabric identifier, hashed with root_ca public key
var deviceid # our own device id bytes(8) little endian
var fabric_label # set by UpdateFabricLabel
# Admin info extracted from NOC/ICAC
var admin_subject
var admin_vendor
# Previous CASE messages for Transcript hash
var _Msg1, _Msg2
# Expiration
var _persist # do we persist this sessions or is it remporary
var expiration # if not `nil` the entry is removed after this timestamp
var __Msg1, __Msg2
# below are placeholders for ongoing transactions or chunked responses
var _chunked_attr_reports # if not `nil` holds a container for the current _chuked_attr_reports
var __chunked_attr_reports # if not `nil` holds a container for the current _chuked_attr_reports
# Group Key Derivation
static var __GROUP_KEY = "GroupKey v1.0" # starting with double `_` means it's not writable
static var _GROUP_KEY = "GroupKey v1.0" # starting with double `_` means it's not writable
#############################################################
def init(store, local_session_id, initiator_session_id)
def init(store, local_session_id, initiator_session_id, fabric)
import crypto
self.__store = store
self._store = store
self.mode = 0
self.local_session_id = local_session_id
self.initiator_session_id = initiator_session_id
self.counter_rcv = matter.Counter()
self.counter_snd = matter.Counter()
self._counter_insecure_rcv = matter.Counter()
self._counter_insecure_snd = matter.Counter()
self.__breadcrumb = 0
self.__exchange_id = crypto.random(2).get(0,2) # generate a random 16 bits number, then increment with rollover
self.__counter_insecure_rcv = matter.Counter()
self.__counter_insecure_snd = matter.Counter()
self._breadcrumb = 0
self._exchange_id = crypto.random(2).get(0,2) # generate a random 16 bits number, then increment with rollover
self._fabric = fabric ? fabric : self._store.create_fabric()
self.update()
end
#############################################################
# Update the timestamp or any other information
def update()
self.last_used = tasmota.rtc()['utc']
end
def set_mode_PASE() self.set_mode(self._PASE) end
def set_mode_CASE() self.set_mode(self._CASE) end
def is_PASE() return self.mode == self._PASE end
def is_CASE() return self.mode == self._CASE end
#############################################################
# Register the frabric as complete (end of commissioning)
def fabric_completed()
self._fabric.set_no_expiration()
self._fabric.set_persist(true)
self._store.add_fabric(self._fabric)
end
#############################################################
# Register the frabric as complete (end of commissioning)
def fabric_candidate()
self._fabric.set_expire_in_seconds(120) # expire in 2 minutes
self._store.add_fabric(self._fabric)
end
#############################################################
# Persist to fabric
# Add self session to the persisted established CASE session of the fabric
def persist_to_fabric()
self._fabric.add_session(self)
end
#############################################################
@ -113,9 +321,8 @@ class Matter_Session
#
def close()
# close the PASE session, it will be re-opened with a CASE session
var persist_save = self._persist
self.local_session_id = self._future_local_session_id
self.initiator_session_id = self._future_initiator_session_id
self.local_session_id = self.__future_local_session_id
self.initiator_session_id = self.__future_initiator_session_id
self.source_node_id = nil
self.counter_rcv.reset()
self.counter_snd.reset()
@ -123,18 +330,14 @@ class Matter_Session
self._i2r_privacy = nil
self.r2ikey = nil
self.attestation_challenge = nil
self.fabric_label = ""
# clear any attribute starting with `_`
# clear any attribute starting with `__`
import introspect
for k : introspect.members(self)
var v = introspect.get(self, k)
if type(v) != 'function' && type(v) != 'instance' && k[0] == '_' && k[1] != '_'
if type(v) != 'function' && type(v) != 'instance' && k[0] == '_' && k[1] == '_'
self.(k) = nil
end
end
self._persist = persist_save
# self._future_initiator_session_id = nil
# self._future_local_session_id = nil
end
#############################################################
@ -146,30 +349,31 @@ class Matter_Session
self._i2r_privacy = nil # clear cache
self.r2ikey = r2i
self.attestation_challenge = ac
self.session_timestamp = st
self.created = st
end
def set_ca(ca)
self.root_ca_certificate = ca
self._fabric.root_ca_certificate = ca
end
def set_noc(noc, icac)
self.noc = noc
self.icac = icac
self._fabric.noc = noc
self._fabric.icac = icac
end
def set_ipk_epoch_key(ipk_epoch_key)
self.ipk_epoch_key = ipk_epoch_key
self._fabric.ipk_epoch_key = ipk_epoch_key
end
def set_fabric_device(fabric, deviceid, fc)
self.fabric = fabric
self.deviceid = deviceid
self.fabric_compressed = fc
self.__store.remove_redundant_session(self)
def set_admin_subject_vendor(admin_subject, admin_vendor)
self._fabric.admin_subject = admin_subject
self._fabric.admin_vendor = admin_vendor
end
def set_persist(p)
self._persist = bool(p)
def set_fabric_device(fabric_id, device_id, fc)
self._fabric.fabric_id = fabric_id
self._fabric.device_id = device_id
self._fabric.fabric_compressed = fc
end
def set_fabric_label(s)
if type(s) == 'string'
self.fabric_label = s
self._fabric.fabric_label = s
end
end
@ -195,75 +399,45 @@ class Matter_Session
return self.attestation_challenge
end
def get_ca()
return self.root_ca_certificate
return self._fabric.root_ca_certificate
end
def get_ca_pub()
if self.root_ca_certificate
var m = matter.TLV.parse(self.root_ca_certificate)
return m.findsubval(9)
return self._fabric.get_ca_pub()
end
end
def get_noc() return self.noc end
def get_icac() return self.icac end
def get_ipk_epoch_key() return self.ipk_epoch_key end
def get_fabric() return self.fabric end
def get_deviceid() return self.deviceid end
def get_fabric_compressed() return self.fabric_compressed end
def get_fabric() return self._fabric end
def get_noc() return self._fabric.noc end
def get_icac() return self._fabric.icac end
def get_ipk_epoch_key() return self._fabric.ipk_epoch_key end
def get_fabric_id() return self._fabric.fabric_id end
def get_device_id() return self._fabric.device_id end
def get_fabric_compressed() return self._fabric.fabric_compressed end
def get_fabric_label() return self._fabric.fabric_label end
def get_admin_subject() return self._fabric.admin_subject end
def get_admin_vendor() return self._fabric.admin_vendor end
#############################################################
# Generate a private key (or retrieve it)
def get_pk()
if !self.no_private_key
if !self._fabric.no_private_key
import crypto
self.no_private_key = crypto.random(32)
self._fabric.no_private_key = crypto.random(32)
end
return self.no_private_key
return self._fabric.no_private_key
end
#############################################################
# Operational Group Key Derivation, 4.15.2, p.182
def get_ipk_group_key()
if self.ipk_epoch_key == nil || self.fabric_compressed == nil return nil end
if self.get_ipk_epoch_key() == nil || self.get_fabric_compressed() == nil return nil end
import crypto
var hk = crypto.HKDF_SHA256()
var info = bytes().fromstring(self.__GROUP_KEY)
var hash = hk.derive(self.ipk_epoch_key, self.fabric_compressed, info, 16)
var info = bytes().fromstring(self._GROUP_KEY)
var hash = hk.derive(self.get_ipk_epoch_key(), self.get_fabric_compressed(), info, 16)
return hash
end
#############################################################
# set absolute time for expiration
def set_no_expiration()
self.expiration = nil
end
#############################################################
# set absolute time for expiration
def set_expire_time(t)
self.expiration = int(t)
end
#############################################################
# set relative time in the future for expiration (in seconds)
def set_expire_in_seconds(s, now)
if s == nil return end
if now == nil now = tasmota.rtc()['utc'] end
self.set_expire_time(now + s)
end
#############################################################
# set relative time in the future for expiration (in seconds)
# returns `true` if expiration date has been reached
def has_expired(now)
if now == nil now = tasmota.rtc()['utc'] end
if self.expiration != nil
return now >= self.expiration
end
return false
end
#############################################################
# to_json()
# Session::to_json()
#
# convert a single entry as json
# returns a JSON string
@ -286,28 +460,26 @@ class Matter_Session
if v == nil continue end
if k == "counter_rcv" v = v.val()
elif k == "counter_snd" v = v.val() + 256 # take a margin to avoid reusing the same counter
elif k == "counter_snd" v = v.next() + 256 # take a margin to avoid reusing the same counter
elif isinstance(v, bytes) v = "$$" + v.tob64() # bytes
elif type(v) == 'instance' continue # skip any other instance
end
if isinstance(v, bytes) v = "$$" + v.tob64() end # bytes
# if isinstance(v, bytes) v = "0x" + v.tohex() end
# if type(v) == 'string' v = string.escape(v, true) end
r.push(string.format("%s:%s", json.dump(str(k)), json.dump(v)))
end
return "{" + r.concat(",") + "}"
end
#############################################################
# fromjson()
# Session::fromjson()
#
# reads a map and load arguments
# returns an new instance of session
#############################################################
static def fromjson(store, values)
static def fromjson(store, values, fabric)
import string
import introspect
var self = matter.Session(store)
var self = matter.Session(store, nil, nil, fabric)
for k:values.keys()
var v = values[k]
@ -335,7 +507,7 @@ class Matter_Session
#############################################################
# Callback to Session store
def save()
self.__store.save()
self._store.save_fabrics()
end
#############################################################
@ -384,16 +556,49 @@ end
matter.Session = Matter_Session
#################################################################################
#################################################################################
#################################################################################
# Matter_Session_Store class
#################################################################################
#################################################################################
#################################################################################
class Matter_Session_Store
var sessions
static var FILENAME = "_matter_sessions.json"
var fabrics # list of provisioned fabrics
static var _FABRICS = "_matter_fabrics.json"
#############################################################
def init()
self.sessions = []
self.sessions = matter.Expirable_list()
self.fabrics = matter.Expirable_list()
end
#############################################################
# add provisioned fabric
def add_fabric(fabric)
if !isinstance(fabric, matter.Fabric) raise "value_error", "must be of class matter.Fabric" end
if self.fabrics.find(fabric) == nil
self.remove_redundant_fabric(fabric)
self.fabrics.push(fabric)
end
end
#############################################################
# Remove redudant fabric
#
# remove all other fabrics that have the same:
# fabric_id / device_id
def remove_redundant_fabric(f)
var i = 0
while i < size(self.fabrics)
var fabric = self.fabrics[i]
if fabric != f && fabric.fabric_id == f.fabric_id && fabric.device_id == f.device_id
self.fabrics.remove(i)
else
i += 1
end
end
end
#############################################################
@ -422,7 +627,11 @@ class Matter_Session_Store
var i = 0
var sessions = self.sessions
while i < sz
if sessions[i].local_session_id == id return sessions[i] end
var session = sessions[i]
if session.local_session_id == id
session.update()
return session
end
i += 1
end
end
@ -434,7 +643,11 @@ class Matter_Session_Store
var i = 0
var sessions = self.sessions
while i < sz
if sessions[i].source_node_id == nodeid return sessions[i] end
var session = sessions[i]
if session.source_node_id == nodeid
session.update()
return session
end
i += 1
end
end
@ -454,24 +667,6 @@ class Matter_Session_Store
end
end
#############################################################
# Remove session by reference
#
# remove all other sessions that have the same:
# fabric / deviceid / fc
def remove_redundant_session(s)
var i = 0
var sessions = self.sessions
while i < size(self.sessions)
var session = sessions[i]
if session != s && session.fabric == s.fabric && session.deviceid == s.deviceid #- && session.fabric_compressed == s.fabric_compressed -#
sessions.remove(i)
else
i += 1
end
end
end
#############################################################
# Generate a new local_session_id
def gen_local_session_id()
@ -489,21 +684,14 @@ class Matter_Session_Store
#############################################################
# remove_expired
#
# Check is any session has expired
def remove_expired()
var dirty = false
var i = 0
var sessions = self.sessions
while i < size(self.sessions)
if sessions[i].has_expired()
if sessions[i]._persist dirty = true end # do we need to save
sessions.remove(i)
else
i += 1
end
end
if dirty self.save() end
self.sessions.every_second()
self.fabrics.every_second()
end
#############################################################
# call remove_expired every second
#
def every_second()
self.remove_expired()
end
@ -519,6 +707,7 @@ class Matter_Session_Store
self.sessions.push(session)
end
session.set_expire_in_seconds(expire)
session.update()
return session
end
@ -529,8 +718,10 @@ class Matter_Session_Store
var i = 0
var sessions = self.sessions
while i < size(sessions)
if sessions[i].resumption_id == resumption_id
return sessions[i]
var session = sessions[i]
if session.resumption_id == resumption_id && session.shared_secret != nil
session.update()
return session
end
i += 1
end
@ -545,7 +736,7 @@ class Matter_Session_Store
var idx = 0
while idx < size(self.sessions)
var session = self.sessions[idx]
if session.get_deviceid() && session.get_fabric()
if session.get_device_id() && session.get_fabric_id()
ret.push(session)
end
idx += 1
@ -554,61 +745,85 @@ class Matter_Session_Store
end
#############################################################
def save()
def save_fabrics()
import json
self.remove_expired() # clean before saving
var sessions_saved = 0
var j = []
for v:self.sessions
if v._persist
j.push(v.tojson())
var fabs = []
for f : self.fabrics.persistables()
for _ : f._sessions.persistables() sessions_saved += 1 end # count persitable sessions
fabs.push(f.tojson())
end
end
var j_size = size(j)
j = "[" + j.concat(",") + "]"
var fabs_size = size(fabs)
fabs = "[" + fabs.concat(",") + "]"
try
import string
var f = open(self.FILENAME, "w")
f.write(j)
var f = open(self._FABRICS, "w")
f.write(fabs)
f.close()
tasmota.log(string.format("MTR: Saved %i session(s)", j_size), 2)
return j
tasmota.log(string.format("MTR: Saved %i fabric(s) and %i session(s)", fabs_size, sessions_saved), 2)
except .. as e, m
tasmota.log("MTR: Session_Store::save Exception:" + str(e) + "|" + str(m), 2)
return j
end
end
#############################################################
def load()
# load fabrics and associated sessions
def load_fabrics()
import string
try
self.sessions = [] # remove any left-over
var f = open(self.FILENAME)
var s = f.read()
self.sessions = matter.Expirable_list() # remove any left-over
self.fabrics = matter.Expirable_list() # remove any left-over
var f = open(self._FABRICS)
var file_content = f.read()
f.close()
import json
var j = json.load(s)
s = nil
var file_json = json.load(file_content)
file_content = nil
tasmota.gc() # clean-up a potential long string
for v:j # iterate on values
var session = matter.Session.fromjson(self, v)
session._persist = true
for v : file_json # iterate on values
# read fabric
var fabric = matter.Fabric.fromjson(self, v)
fabric.set_no_expiration()
fabric.set_persist(true)
# iterate on sessions
var sessions_json = v.find("_sessions", [])
for sess_json : sessions_json
var session = matter.Session.fromjson(self, sess_json, fabric)
if session != nil
session.set_no_expiration()
session.set_persist(true)
self.add_session(session)
fabric.add_session(session)
end
end
tasmota.log(string.format("MTR: Loaded %i session(s)", size(self.sessions)), 2)
self.fabrics.push(fabric)
end
tasmota.log(string.format("MTR: Loaded %i fabric(s)", size(self.fabrics)), 2)
except .. as e, m
if e != "io_error"
tasmota.log("MTR: Session_Store::load Exception:" + str(e) + "|" + str(m), 2)
end
end
self.remove_expired() # clean after load
# persistables are normally not expiring
# if self.remove_expired() # clean after load
# self.save_fabrics()
# end
end
#############################################################
def create_fabric()
var fabric = matter.Fabric(self)
return fabric
end
end
matter.Session_Store = Matter_Session_Store

View File

@ -92,14 +92,14 @@ class Matter_UDPServer
var listening # true if active
var udp_socket
var dispatch_cb # callback to call when a message is received
var packets_sent # map of packets sent to be acknowledged
var packets_sent # list map of packets sent to be acknowledged
#############################################################
def init(address, port)
self.address = address ? address : ""
self.port = port ? port : 5540
self.listening = false
self.packets_sent = {}
self.packets_sent = []
end
#############################################################
@ -157,18 +157,23 @@ class Matter_UDPServer
#############################################################
def resend_packets()
for packet:self.packets_sent
var idx = 0
while idx < size(self.packets_sent)
var packet = self.packets_sent[idx]
if tasmota.time_reached(packet.next_try)
if packet.retries <= self.RETRIES
tasmota.log("MTR: resending packet id=" + str(packet.msg_id), 3)
packet.send(self.udp_socket) # resend
packet.next_try = tasmota.millis() + packet.backoff_time(packet.retries)
packet.retries += 1
idx += 1
else
import string
self.packets_sent.remove(packet.msg_id)
self.packets_sent.remove(idx)
tasmota.log(string.format("MTR: target unreachable '[%s]:%i' msg_id=%i", packet.addr, packet.port, packet.msg_id), 2)
end
else
idx += 1
end
end
end
@ -177,9 +182,14 @@ class Matter_UDPServer
# just received acknowledgment, remove packet from sender
def packet_ack(id)
if id == nil return end
if self.packets_sent.contains(id)
self.packets_sent.remove(id)
var idx = 0
while idx < size(self.packets_sent)
if self.packets_sent[idx].msg_id == id
self.packets_sent.remove(idx)
tasmota.log("MTR: removed packet from sending list id=" + str(id), 4)
else
idx += 1
end
end
end
@ -188,7 +198,7 @@ class Matter_UDPServer
var packet = matter.UDPPacket_sent(raw, addr, port, id)
packet.send(self.udp_socket) # send
if id
self.packets_sent[id] = packet
self.packets_sent.push(packet)
end
end

View File

@ -76,6 +76,54 @@ class Matter_UI
return matter_enabled
end
#- ---------------------------------------------------------------------- -#
#- Show QR Code
#- ---------------------------------------------------------------------- -#
def show_qrcode(qr_text)
import webserver
# QRCode via UTF8
var empty = " "
var lowhalf = "\342\226\204"
var uphalf = "\342\226\200"
var full = "\342\226\210"
var qr = matter.QRCode.encode_str(qr_text)
var bitmap = qr['bitmap']
var sz = qr['size']
webserver.content_send('<style>.qr{font-family:monospace; margin:0; padding:0; white-space:pre; font-size:18px; color:#fff; line-height:100%;}</style>')
webserver.content_send("<div style='transform:scale(.8,1); display:inline-block;'>")
var s = "<div class='qr'>"
webserver.content_send(s)
s = ""
for i: 0 .. sz + 1 s += lowhalf end
s += "</div>"
webserver.content_send(s)
for i: 0 .. (sz+1)/2 - 1
s = "<div class='qr' style='background-color:#000;'>" + full
for j: 0 .. sz - 1
var high = (bitmap[i*2][j] == " ")
var low = (i*2+1 < sz) ? (bitmap[i*2+1][j] == " ") : true # default to true for bottom margin if size is odd
s += high ? (low ? full : uphalf) : (low ? lowhalf : empty)
end
s += full
s += "</div>"
webserver.content_send(s)
end
# webserver.content_send("</div>")
if sz % 2 == 0
s = "<div class='qr' style='background-color:#000;'>"
for i: 0 .. sz + 1 s += uphalf end
s += "/<div>"
webserver.content_send(s)
end
webserver.content_send("</div>")
end
#- ---------------------------------------------------------------------- -#
#- Show commissioning information and QR Code
#- ---------------------------------------------------------------------- -#
@ -83,16 +131,31 @@ class Matter_UI
import webserver
import string
webserver.content_send("<fieldset><legend><b>&nbsp;Matter Passcode&nbsp;</b></legend><p></p>")
var seconds_left = (self.device.commissioning_open - tasmota.millis()) / 1000
if seconds_left < 0 seconds_left = 0 end
var min_left = (seconds_left + 30) / 60
webserver.content_send(string.format("<fieldset><legend><b>&nbsp;[ Commissioning open for %i min ]&nbsp;</b></legend><p></p>", min_left))
var pairing_code = self.device.compute_manual_pairing_code()
webserver.content_send(string.format("<p>Manual pairing code:<br><b>%s-%s-%s</b></p><hr>", pairing_code[0..3], pairing_code[4..6], pairing_code[7..]))
var qr_text = self.device.compute_qrcode_content()
webserver.content_send('<div id="qrcode"></div>')
webserver.content_send(string.format('<script type="text/javascript"> new QRCode(document.getElementById("qrcode"), "%s");</script>', qr_text))
webserver.content_send(string.format("<p>%s</p><hr>", qr_text))
self.show_qrcode(qr_text)
webserver.content_send(string.format("<p> %s</p>", qr_text))
webserver.content_send("<p></p></fieldset><p></p>")
end
#- ---------------------------------------------------------------------- -#
#- Show Passcode / discriminator form
#- ---------------------------------------------------------------------- -#
def show_passcode_form()
import webserver
import string
webserver.content_send("<fieldset><legend><b>&nbsp;Matter Passcode&nbsp;</b></legend><p></p>")
webserver.content_send("<form action='/matterc' method='post' >")
webserver.content_send("<p>Passcode:</p>")
webserver.content_send(string.format("<input type='number' min='1' max='99999998' name='passcode' value='%i'>", self.device.passcode))
@ -100,8 +163,6 @@ class Matter_UI
webserver.content_send(string.format("<input type='number' min='0' max='2047' name='discriminator' value='%i'>", self.device.discriminator))
webserver.content_send(string.format("<p><input type='checkbox' name='ipv4'%s>IPv4 only</p>", self.device.ipv4only ? " checked" : ""))
webserver.content_send("<p></p><button name='passcode' class='button bgrn'>Change</button></form></p>")
webserver.content_send("<p></p></fieldset><p></p>")
end
@ -109,54 +170,41 @@ class Matter_UI
#- ---------------------------------------------------------------------- -#
#- Show commissioning information and QR Code
#- ---------------------------------------------------------------------- -#
def show_session_info(p)
def show_fabric_info(p)
import webserver
import string
webserver.content_send("<fieldset><legend><b>&nbsp;Sessions&nbsp;</b></legend><p></p>")
webserver.content_send("<p>Existing sessions:</p>")
webserver.content_send("<fieldset><legend><b>&nbsp;Fabrics&nbsp;</b></legend><p></p>")
webserver.content_send("<p>Existing fabrics:</p>")
if size(self.device.sessions.sessions) == 0
webserver.content_send("<p><b>None</b></p>")
else
var i = 0
var sz = size(self.device.sessions.sessions)
while i < sz
var s = self.device.sessions.sessions[i]
if s.fabric
webserver.content_send(string.format("<fieldset><legend><b>&nbsp;Session %i&nbsp;</b></legend><p></p>", s.local_session_id))
if i != 0 webserver.content_send("<hr>") end
var fabric_rev = s.fabric.copy().reverse()
var deviceid_rev = s.deviceid.copy().reverse()
var first = true
for f : self.device.sessions.fabrics.persistables()
if !first webserver.content_send("<hr>") end
first = false
webserver.content_send(string.format("<fieldset><legend><b>&nbsp;%s&nbsp;</b></legend><p></p>", "&lt;No label&gt;"))
var fabric_rev = f.get_fabric_id().copy().reverse()
var deviceid_rev = f.get_device_id().copy().reverse()
webserver.content_send(string.format("Fabric: %s<br>", fabric_rev.tohex()))
webserver.content_send(string.format("Device: %s<br>&nbsp;", deviceid_rev.tohex()))
webserver.content_send("<form action='/matterc' method='post' ")
webserver.content_send("onsubmit='return confirm(\"This will cause a restart.\");'>")
webserver.content_send(string.format("<input name='del_session' type='hidden' value='%d'>", s.local_session_id))
webserver.content_send("<button name='del' class='button bgrn'>Delete Session</button></form></p>")
webserver.content_send(string.format("<input name='del_fabric' type='hidden' value='%s'>", fabric_rev.tohex()))
webserver.content_send("<button name='del' class='button bgrn'>Delete Fabric</button></form></p>")
webserver.content_send("<p></p></fieldset><p></p>")
end
i += 1
end
end
webserver.content_send("<p></p></fieldset><p></p>")
end
#######################################################################
# Serve qrcode.min.js static file
#######################################################################
def page_qrcode_min_js()
import webserver
webserver.content_open(200, "text/javascript")
webserver.content_send(matter._QRCODE_MINJS)
end
#######################################################################
# Display the complete page
#######################################################################
@ -169,11 +217,9 @@ class Matter_UI
webserver.content_start("Matter") #- title of the web page -#
webserver.content_send_style() #- send standard Tasmota styles -#
webserver.content_send('<script type="text/javascript" src="qrcode.min.js"></script>')
if self.show_enable()
self.show_commissioning_info()
self.show_session_info()
self.show_passcode_form()
self.show_fabric_info()
end
webserver.content_button(webserver.BUTTON_CONFIGURATION)
webserver.content_stop() #- end of web page -#
@ -222,12 +268,21 @@ class Matter_UI
#- and force restart -#
webserver.redirect("/?rst=")
elif webserver.has_arg("del_session")
var session = self.device.sessions.get_session_by_local_session_id(int(webserver.arg("del_session")))
if session != nil
self.device.sessions.remove_session(session)
self.device.sessions.save()
elif webserver.has_arg("del_fabric")
var del_fabric = webserver.arg("del_fabric")
var idx = 0
var fabrics = self.device.sessions.fabrics
var dirty = false
while idx < size(fabrics)
var fabric_hex = fabrics[idx].get_fabric_id().copy().reverse().tohex()
if fabric_hex == del_fabric
fabrics.remove(idx)
dirty = true
else
idx += 1
end
end
if dirty self.device.sessions.save_fabrics() end
#- and force restart -#
webserver.redirect("/?rst=")
@ -246,6 +301,38 @@ class Matter_UI
end
end
#- display sensor value in the web UI -#
def web_sensor()
import webserver
import string
var matter_enabled = tasmota.get_option(matter.MATTER_OPTION)
if matter_enabled
if self.device.commissioning_open
self.show_commissioning_info()
end
# mtc0 = close, mtc1 = open commissioning
webserver.content_send(string.format("<button onclick='la(\"&mtc%i=1\");'>", self.device.commissioning_open == nil ? 1 : 0))
webserver.content_send(matter._LOGO)
if self.device.commissioning_open == nil
webserver.content_send(" Open Commissioning</button>")
else
webserver.content_send(" Close Commissioning</button>")
end
end
end
def web_get_arg()
import webserver
if webserver.has_arg("mtc0") # Close Commissioning
self.device.stop_basic_commissioning()
elif webserver.has_arg("mtc1") # Open Commissioning
self.device.start_basic_commissioning()
end
end
#- ---------------------------------------------------------------------- -#
# respond to web_add_handler() event to register web listeners
#- ---------------------------------------------------------------------- -#
@ -255,7 +342,6 @@ class Matter_UI
#- we need to register a closure, not just a function, that captures the current instance -#
webserver.on("/matterc", / -> self.page_part_mgr(), webserver.HTTP_GET)
webserver.on("/matterc", / -> self.page_part_ctl(), webserver.HTTP_POST)
webserver.on("/qrcode.min.js", / -> self.page_qrcode_min_js(), webserver.HTTP_GET)
end
end
matter.UI = Matter_UI

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,319 @@
/*
* QR Code generator library (C)
*
* Copyright (c) Project Nayuki. (MIT License)
* https://www.nayuki.io/page/qr-code-generator-library
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
* - The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* - The Software is provided "as is", without warranty of any kind, express or
* implied, including but not limited to the warranties of merchantability,
* fitness for a particular purpose and noninfringement. In no event shall the
* authors or copyright holders be liable for any claim, damages or other
* liability, whether in an action of contract, tort or otherwise, arising from,
* out of or in connection with the Software or the use or other dealings in the
* Software.
*/
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* This library creates QR Code symbols, which is a type of two-dimension barcode.
* Invented by Denso Wave and described in the ISO/IEC 18004 standard.
* A QR Code structure is an immutable square grid of black and white cells.
* The library provides functions to create a QR Code from text or binary data.
* The library covers the QR Code Model 2 specification, supporting all versions (sizes)
* from 1 to 40, all 4 error correction levels, and 4 character encoding modes.
*
* Ways to create a QR Code object:
* - High level: Take the payload data and call qrcodegen_encodeText() or qrcodegen_encodeBinary().
* - Low level: Custom-make the list of segments and call
* qrcodegen_encodeSegments() or qrcodegen_encodeSegmentsAdvanced().
* (Note that all ways require supplying the desired error correction level and various byte buffers.)
*/
/*---- Enum and struct types----*/
/*
* The error correction level in a QR Code symbol.
*/
enum qrcodegen_Ecc {
// Must be declared in ascending order of error protection
// so that an internal qrcodegen function works properly
qrcodegen_Ecc_LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords
qrcodegen_Ecc_MEDIUM , // The QR Code can tolerate about 15% erroneous codewords
qrcodegen_Ecc_QUARTILE, // The QR Code can tolerate about 25% erroneous codewords
qrcodegen_Ecc_HIGH , // The QR Code can tolerate about 30% erroneous codewords
};
/*
* The mask pattern used in a QR Code symbol.
*/
enum qrcodegen_Mask {
// A special value to tell the QR Code encoder to
// automatically select an appropriate mask pattern
qrcodegen_Mask_AUTO = -1,
// The eight actual mask patterns
qrcodegen_Mask_0 = 0,
qrcodegen_Mask_1,
qrcodegen_Mask_2,
qrcodegen_Mask_3,
qrcodegen_Mask_4,
qrcodegen_Mask_5,
qrcodegen_Mask_6,
qrcodegen_Mask_7,
};
/*
* Describes how a segment's data bits are interpreted.
*/
enum qrcodegen_Mode {
qrcodegen_Mode_NUMERIC = 0x1,
qrcodegen_Mode_ALPHANUMERIC = 0x2,
qrcodegen_Mode_BYTE = 0x4,
qrcodegen_Mode_KANJI = 0x8,
qrcodegen_Mode_ECI = 0x7,
};
/*
* A segment of character/binary/control data in a QR Code symbol.
* The mid-level way to create a segment is to take the payload data
* and call a factory function such as qrcodegen_makeNumeric().
* The low-level way to create a segment is to custom-make the bit buffer
* and initialize a qrcodegen_Segment struct with appropriate values.
* Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.
* Any segment longer than this is meaningless for the purpose of generating QR Codes.
* Moreover, the maximum allowed bit length is 32767 because
* the largest QR Code (version 40) has 31329 modules.
*/
struct qrcodegen_Segment {
// The mode indicator of this segment.
enum qrcodegen_Mode mode;
// The length of this segment's unencoded data. Measured in characters for
// numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
// Always zero or positive. Not the same as the data's bit length.
int numChars;
// The data bits of this segment, packed in bitwise big endian.
// Can be null if the bit length is zero.
uint8_t *data;
// The number of valid data bits used in the buffer. Requires
// 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8.
// The character count (numChars) must agree with the mode and the bit buffer length.
int bitLength;
};
/*---- Macro constants and functions ----*/
#define qrcodegen_VERSION_MIN 1 // The minimum version number supported in the QR Code Model 2 standard
#define qrcodegen_VERSION_MAX 40 // The maximum version number supported in the QR Code Model 2 standard
// Calculates the number of bytes needed to store any QR Code up to and including the given version number,
// as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];'
// can store any single QR Code from version 1 to 25 (inclusive). The result fits in an int (or int16).
// Requires qrcodegen_VERSION_MIN <= n <= qrcodegen_VERSION_MAX.
#define qrcodegen_BUFFER_LEN_FOR_VERSION(n) ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) / 8 + 1)
// The worst-case number of bytes needed to store one QR Code, up to and including
// version 40. This value equals 3918, which is just under 4 kilobytes.
// Use this more convenient value to avoid calculating tighter memory bounds for buffers.
#define qrcodegen_BUFFER_LEN_MAX qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX)
/*---- Functions (high level) to generate QR Codes ----*/
/*
* Encodes the given text string to a QR Code, returning true if encoding succeeded.
* If the data is too long to fit in any version in the given range
* at the given ECC level, then false is returned.
* - The input text must be encoded in UTF-8 and contain no NULs.
* - The variables ecl and mask must correspond to enum constant values.
* - Requires 1 <= minVersion <= maxVersion <= 40.
* - The arrays tempBuffer and qrcode must each have a length
* of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion).
* - After the function returns, tempBuffer contains no useful data.
* - If successful, the resulting QR Code may use numeric,
* alphanumeric, or byte mode to encode the text.
* - In the most optimistic case, a QR Code at version 40 with low ECC
* can hold any UTF-8 string up to 2953 bytes, or any alphanumeric string
* up to 4296 characters, or any digit string up to 7089 characters.
* These numbers represent the hard upper limit of the QR Code standard.
* - Please consult the QR Code specification for information on
* data capacities per version, ECC level, and text encoding mode.
*/
bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[],
enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl);
/*
* Encodes the given binary data to a QR Code, returning true if encoding succeeded.
* If the data is too long to fit in any version in the given range
* at the given ECC level, then false is returned.
* - The input array range dataAndTemp[0 : dataLen] should normally be
* valid UTF-8 text, but is not required by the QR Code standard.
* - The variables ecl and mask must correspond to enum constant values.
* - Requires 1 <= minVersion <= maxVersion <= 40.
* - The arrays dataAndTemp and qrcode must each have a length
* of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion).
* - After the function returns, the contents of dataAndTemp may have changed,
* and does not represent useful data anymore.
* - If successful, the resulting QR Code will use byte mode to encode the data.
* - In the most optimistic case, a QR Code at version 40 with low ECC can hold any byte
* sequence up to length 2953. This is the hard upper limit of the QR Code standard.
* - Please consult the QR Code specification for information on
* data capacities per version, ECC level, and text encoding mode.
*/
bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[],
enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl);
/*---- Functions (low level) to generate QR Codes ----*/
/*
* Renders a QR Code representing the given segments at the given error correction level.
* The smallest possible QR Code version is automatically chosen for the output. Returns true if
* QR Code creation succeeded, or false if the data is too long to fit in any version. The ECC level
* of the result may be higher than the ecl argument if it can be done without increasing the version.
* This function allows the user to create a custom sequence of segments that switches
* between modes (such as alphanumeric and byte) to encode text in less space.
* This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary().
* To save memory, the segments' data buffers can alias/overlap tempBuffer, and will
* result in them being clobbered, but the QR Code output will still be correct.
* But the qrcode array must not overlap tempBuffer or any segment's data buffer.
*/
bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len,
enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]);
/*
* Renders a QR Code representing the given segments with the given encoding parameters.
* Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions.
* The smallest possible QR Code version within the given range is automatically
* chosen for the output. Iff boostEcl is true, then the ECC level of the result
* may be higher than the ecl argument if it can be done without increasing the
* version. The mask number is either between 0 to 7 (inclusive) to force that
* mask, or -1 to automatically choose an appropriate mask (which may be slow).
* This function allows the user to create a custom sequence of segments that switches
* between modes (such as alphanumeric and byte) to encode text in less space.
* This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary().
* To save memory, the segments' data buffers can alias/overlap tempBuffer, and will
* result in them being clobbered, but the QR Code output will still be correct.
* But the qrcode array must not overlap tempBuffer or any segment's data buffer.
*/
bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl,
int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]);
/*
* Tests whether the given string can be encoded as a segment in alphanumeric mode.
* A string is encodable iff each character is in the following set: 0 to 9, A to Z
* (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
*/
bool qrcodegen_isAlphanumeric(const char *text);
/*
* Tests whether the given string can be encoded as a segment in numeric mode.
* A string is encodable iff each character is in the range 0 to 9.
*/
bool qrcodegen_isNumeric(const char *text);
/*
* Returns the number of bytes (uint8_t) needed for the data buffer of a segment
* containing the given number of characters using the given mode. Notes:
* - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or
* the number of needed bits exceeds INT16_MAX (i.e. 32767).
* - Otherwise, all valid results are in the range [0, ceil(INT16_MAX / 8)], i.e. at most 4096.
* - It is okay for the user to allocate more bytes for the buffer than needed.
* - For byte mode, numChars measures the number of bytes, not Unicode code points.
* - For ECI mode, numChars must be 0, and the worst-case number of bytes is returned.
* An actual ECI segment can have shorter data. For non-ECI modes, the result is exact.
*/
size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars);
/*
* Returns a segment representing the given binary data encoded in
* byte mode. All input byte arrays are acceptable. Any text string
* can be converted to UTF-8 bytes and encoded as a byte mode segment.
*/
struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]);
/*
* Returns a segment representing the given string of decimal digits encoded in numeric mode.
*/
struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]);
/*
* Returns a segment representing the given text string encoded in alphanumeric mode.
* The characters allowed are: 0 to 9, A to Z (uppercase only), space,
* dollar, percent, asterisk, plus, hyphen, period, slash, colon.
*/
struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]);
/*
* Returns a segment representing an Extended Channel Interpretation
* (ECI) designator with the given assignment value.
*/
struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]);
/*---- Functions to extract raw data from QR Codes ----*/
/*
* Returns the side length of the given QR Code, assuming that encoding succeeded.
* The result is in the range [21, 177]. Note that the length of the array buffer
* is related to the side length - every 'uint8_t qrcode[]' must have length at least
* qrcodegen_BUFFER_LEN_FOR_VERSION(version), which equals ceil(size^2 / 8 + 1).
*/
int qrcodegen_getSize(const uint8_t qrcode[]);
/*
* Returns the color of the module (pixel) at the given coordinates, which is false
* for white or true for black. The top left corner has the coordinates (x=0, y=0).
* If the given coordinates are out of bounds, then false (white) is returned.
*/
bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y);
/*
* Returns the qrcode size of the specified version. Returns -1 on failure
*/
int qrcodegen_version2size(int version);
/*
* Returns the min version of the data that can be stored. Returns -1 on failure
*/
int qrcodegen_getMinFitVersion(enum qrcodegen_Ecc ecl, size_t dataLen);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,659 @@
/* Solidification of Matter_Expirable.h */
/********************************************************************\
* Generated code, don't edit *
\********************************************************************/
#include "be_constobj.h"
extern const bclass be_class_Matter_Expirable;
/********************************************************************
** Solidified function: init
********************************************************************/
be_local_closure(Matter_Expirable_init, /* name */
be_nested_proto(
2, /* nstack */
1, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 1]) { /* constants */
/* K0 */ be_nested_str_weak(_persist),
}),
be_str_weak(init),
&be_const_str_solidified,
( &(const binstruction[ 3]) { /* code */
0x50040000, // 0000 LDBOOL R1 0 0
0x90020001, // 0001 SETMBR R0 K0 R1
0x80000000, // 0002 RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: set_persist
********************************************************************/
be_local_closure(Matter_Expirable_set_persist, /* name */
be_nested_proto(
4, /* nstack */
2, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 1]) { /* constants */
/* K0 */ be_nested_str_weak(_persist),
}),
be_str_weak(set_persist),
&be_const_str_solidified,
( &(const binstruction[ 5]) { /* code */
0x60080017, // 0000 GETGBL R2 G23
0x5C0C0200, // 0001 MOVE R3 R1
0x7C080200, // 0002 CALL R2 1
0x90020002, // 0003 SETMBR R0 K0 R2
0x80000000, // 0004 RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: has_expired
********************************************************************/
be_local_closure(Matter_Expirable_has_expired, /* name */
be_nested_proto(
4, /* nstack */
2, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 4]) { /* constants */
/* K0 */ be_nested_str_weak(tasmota),
/* K1 */ be_nested_str_weak(rtc),
/* K2 */ be_nested_str_weak(utc),
/* K3 */ be_nested_str_weak(_expiration),
}),
be_str_weak(has_expired),
&be_const_str_solidified,
( &(const binstruction[16]) { /* code */
0x4C080000, // 0000 LDNIL R2
0x1C080202, // 0001 EQ R2 R1 R2
0x780A0003, // 0002 JMPF R2 #0007
0xB80A0000, // 0003 GETNGBL R2 K0
0x8C080501, // 0004 GETMET R2 R2 K1
0x7C080200, // 0005 CALL R2 1
0x94040502, // 0006 GETIDX R1 R2 K2
0x88080103, // 0007 GETMBR R2 R0 K3
0x4C0C0000, // 0008 LDNIL R3
0x20080403, // 0009 NE R2 R2 R3
0x780A0002, // 000A JMPF R2 #000E
0x88080103, // 000B GETMBR R2 R0 K3
0x28080202, // 000C GE R2 R1 R2
0x80040400, // 000D RET 1 R2
0x50080000, // 000E LDBOOL R2 0 0
0x80040400, // 000F RET 1 R2
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: get_parent_list
********************************************************************/
be_local_closure(Matter_Expirable_get_parent_list, /* name */
be_nested_proto(
2, /* nstack */
1, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 1]) { /* constants */
/* K0 */ be_nested_str_weak(_list),
}),
be_str_weak(get_parent_list),
&be_const_str_solidified,
( &(const binstruction[ 2]) { /* code */
0x88040100, // 0000 GETMBR R1 R0 K0
0x80040200, // 0001 RET 1 R1
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: persist_post
********************************************************************/
be_local_closure(Matter_Expirable_persist_post, /* name */
be_nested_proto(
1, /* nstack */
1, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
0, /* has constants */
NULL, /* no const */
be_str_weak(persist_post),
&be_const_str_solidified,
( &(const binstruction[ 1]) { /* code */
0x80000000, // 0000 RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: set_expire_time
********************************************************************/
be_local_closure(Matter_Expirable_set_expire_time, /* name */
be_nested_proto(
4, /* nstack */
2, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 1]) { /* constants */
/* K0 */ be_nested_str_weak(_expiration),
}),
be_str_weak(set_expire_time),
&be_const_str_solidified,
( &(const binstruction[ 5]) { /* code */
0x60080009, // 0000 GETGBL R2 G9
0x5C0C0200, // 0001 MOVE R3 R1
0x7C080200, // 0002 CALL R2 1
0x90020002, // 0003 SETMBR R0 K0 R2
0x80000000, // 0004 RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: persist_pre
********************************************************************/
be_local_closure(Matter_Expirable_persist_pre, /* name */
be_nested_proto(
1, /* nstack */
1, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
0, /* has constants */
NULL, /* no const */
be_str_weak(persist_pre),
&be_const_str_solidified,
( &(const binstruction[ 1]) { /* code */
0x80000000, // 0000 RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: hydrate_post
********************************************************************/
be_local_closure(Matter_Expirable_hydrate_post, /* name */
be_nested_proto(
1, /* nstack */
1, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
0, /* has constants */
NULL, /* no const */
be_str_weak(hydrate_post),
&be_const_str_solidified,
( &(const binstruction[ 1]) { /* code */
0x80000000, // 0000 RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: does_persist
********************************************************************/
be_local_closure(Matter_Expirable_does_persist, /* name */
be_nested_proto(
2, /* nstack */
1, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 1]) { /* constants */
/* K0 */ be_nested_str_weak(_persist),
}),
be_str_weak(does_persist),
&be_const_str_solidified,
( &(const binstruction[ 2]) { /* code */
0x88040100, // 0000 GETMBR R1 R0 K0
0x80040200, // 0001 RET 1 R1
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: set_expire_in_seconds
********************************************************************/
be_local_closure(Matter_Expirable_set_expire_in_seconds, /* name */
be_nested_proto(
6, /* nstack */
3, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 4]) { /* constants */
/* K0 */ be_nested_str_weak(tasmota),
/* K1 */ be_nested_str_weak(rtc),
/* K2 */ be_nested_str_weak(utc),
/* K3 */ be_nested_str_weak(set_expire_time),
}),
be_str_weak(set_expire_in_seconds),
&be_const_str_solidified,
( &(const binstruction[15]) { /* code */
0x4C0C0000, // 0000 LDNIL R3
0x1C0C0203, // 0001 EQ R3 R1 R3
0x780E0000, // 0002 JMPF R3 #0004
0x80000600, // 0003 RET 0
0x4C0C0000, // 0004 LDNIL R3
0x1C0C0403, // 0005 EQ R3 R2 R3
0x780E0003, // 0006 JMPF R3 #000B
0xB80E0000, // 0007 GETNGBL R3 K0
0x8C0C0701, // 0008 GETMET R3 R3 K1
0x7C0C0200, // 0009 CALL R3 1
0x94080702, // 000A GETIDX R2 R3 K2
0x8C0C0103, // 000B GETMET R3 R0 K3
0x00140401, // 000C ADD R5 R2 R1
0x7C0C0400, // 000D CALL R3 2
0x80000000, // 000E RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: set_parent_list
********************************************************************/
be_local_closure(Matter_Expirable_set_parent_list, /* name */
be_nested_proto(
2, /* nstack */
2, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 1]) { /* constants */
/* K0 */ be_nested_str_weak(_list),
}),
be_str_weak(set_parent_list),
&be_const_str_solidified,
( &(const binstruction[ 2]) { /* code */
0x90020001, // 0000 SETMBR R0 K0 R1
0x80000000, // 0001 RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: set_no_expiration
********************************************************************/
be_local_closure(Matter_Expirable_set_no_expiration, /* name */
be_nested_proto(
2, /* nstack */
1, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 1]) { /* constants */
/* K0 */ be_nested_str_weak(_expiration),
}),
be_str_weak(set_no_expiration),
&be_const_str_solidified,
( &(const binstruction[ 3]) { /* code */
0x4C040000, // 0000 LDNIL R1
0x90020001, // 0001 SETMBR R0 K0 R1
0x80000000, // 0002 RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified class: Matter_Expirable
********************************************************************/
be_local_class(Matter_Expirable,
3,
NULL,
be_nested_map(15,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_const_key_weak(init, -1), be_const_closure(Matter_Expirable_init_closure) },
{ be_const_key_weak(set_persist, -1), be_const_closure(Matter_Expirable_set_persist_closure) },
{ be_const_key_weak(has_expired, 7), be_const_closure(Matter_Expirable_has_expired_closure) },
{ be_const_key_weak(_persist, -1), be_const_var(1) },
{ be_const_key_weak(get_parent_list, 13), be_const_closure(Matter_Expirable_get_parent_list_closure) },
{ be_const_key_weak(persist_post, -1), be_const_closure(Matter_Expirable_persist_post_closure) },
{ be_const_key_weak(set_expire_time, -1), be_const_closure(Matter_Expirable_set_expire_time_closure) },
{ be_const_key_weak(_list, -1), be_const_var(0) },
{ be_const_key_weak(does_persist, -1), be_const_closure(Matter_Expirable_does_persist_closure) },
{ be_const_key_weak(hydrate_post, 8), be_const_closure(Matter_Expirable_hydrate_post_closure) },
{ be_const_key_weak(set_expire_in_seconds, 3), be_const_closure(Matter_Expirable_set_expire_in_seconds_closure) },
{ be_const_key_weak(_expiration, 10), be_const_var(2) },
{ be_const_key_weak(set_parent_list, -1), be_const_closure(Matter_Expirable_set_parent_list_closure) },
{ be_const_key_weak(persist_pre, -1), be_const_closure(Matter_Expirable_persist_pre_closure) },
{ be_const_key_weak(set_no_expiration, -1), be_const_closure(Matter_Expirable_set_no_expiration_closure) },
})),
be_str_weak(Matter_Expirable)
);
/*******************************************************************/
void be_load_Matter_Expirable_class(bvm *vm) {
be_pushntvclass(vm, &be_class_Matter_Expirable);
be_setglobal(vm, "Matter_Expirable");
be_pop(vm, 1);
}
extern const bclass be_class_Matter_Expirable_list;
/********************************************************************
** Solidified function: every_second
********************************************************************/
be_local_closure(Matter_Expirable_list_every_second, /* name */
be_nested_proto(
3, /* nstack */
1, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 1]) { /* constants */
/* K0 */ be_nested_str_weak(remove_expired),
}),
be_str_weak(every_second),
&be_const_str_solidified,
( &(const binstruction[ 3]) { /* code */
0x8C040100, // 0000 GETMET R1 R0 K0
0x7C040200, // 0001 CALL R1 1
0x80000000, // 0002 RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: setitem
********************************************************************/
be_local_closure(Matter_Expirable_list_setitem, /* name */
be_nested_proto(
7, /* nstack */
3, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 6]) { /* constants */
/* K0 */ be_nested_str_weak(matter),
/* K1 */ be_nested_str_weak(Expirable),
/* K2 */ be_nested_str_weak(type_error),
/* K3 */ be_nested_str_weak(argument_X20must_X20be_X20of_X20class_X20_X27Expirable_X27),
/* K4 */ be_nested_str_weak(set_parent_list),
/* K5 */ be_nested_str_weak(setitem),
}),
be_str_weak(setitem),
&be_const_str_solidified,
( &(const binstruction[18]) { /* code */
0x600C000F, // 0000 GETGBL R3 G15
0x5C100400, // 0001 MOVE R4 R2
0xB8160000, // 0002 GETNGBL R5 K0
0x88140B01, // 0003 GETMBR R5 R5 K1
0x7C0C0400, // 0004 CALL R3 2
0x740E0000, // 0005 JMPT R3 #0007
0xB0060503, // 0006 RAISE 1 K2 K3
0x8C0C0504, // 0007 GETMET R3 R2 K4
0x5C140000, // 0008 MOVE R5 R0
0x7C0C0400, // 0009 CALL R3 2
0x600C0003, // 000A GETGBL R3 G3
0x5C100000, // 000B MOVE R4 R0
0x7C0C0200, // 000C CALL R3 1
0x8C0C0705, // 000D GETMET R3 R3 K5
0x5C140200, // 000E MOVE R5 R1
0x5C180400, // 000F MOVE R6 R2
0x7C0C0600, // 0010 CALL R3 3
0x80040600, // 0011 RET 1 R3
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: push
********************************************************************/
be_local_closure(Matter_Expirable_list_push, /* name */
be_nested_proto(
5, /* nstack */
2, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 6]) { /* constants */
/* K0 */ be_nested_str_weak(matter),
/* K1 */ be_nested_str_weak(Expirable),
/* K2 */ be_nested_str_weak(type_error),
/* K3 */ be_nested_str_weak(argument_X20must_X20be_X20of_X20class_X20_X27Expirable_X27),
/* K4 */ be_nested_str_weak(set_parent_list),
/* K5 */ be_nested_str_weak(push),
}),
be_str_weak(push),
&be_const_str_solidified,
( &(const binstruction[17]) { /* code */
0x6008000F, // 0000 GETGBL R2 G15
0x5C0C0200, // 0001 MOVE R3 R1
0xB8120000, // 0002 GETNGBL R4 K0
0x88100901, // 0003 GETMBR R4 R4 K1
0x7C080400, // 0004 CALL R2 2
0x740A0000, // 0005 JMPT R2 #0007
0xB0060503, // 0006 RAISE 1 K2 K3
0x8C080304, // 0007 GETMET R2 R1 K4
0x5C100000, // 0008 MOVE R4 R0
0x7C080400, // 0009 CALL R2 2
0x60080003, // 000A GETGBL R2 G3
0x5C0C0000, // 000B MOVE R3 R0
0x7C080200, // 000C CALL R2 1
0x8C080505, // 000D GETMET R2 R2 K5
0x5C100200, // 000E MOVE R4 R1
0x7C080400, // 000F CALL R2 2
0x80040400, // 0010 RET 1 R2
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: remove_expired
********************************************************************/
be_local_closure(Matter_Expirable_list_remove_expired, /* name */
be_nested_proto(
6, /* nstack */
1, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 5]) { /* constants */
/* K0 */ be_const_int(0),
/* K1 */ be_nested_str_weak(has_expired),
/* K2 */ be_nested_str_weak(_persist),
/* K3 */ be_nested_str_weak(remove),
/* K4 */ be_const_int(1),
}),
be_str_weak(remove_expired),
&be_const_str_solidified,
( &(const binstruction[22]) { /* code */
0x50040000, // 0000 LDBOOL R1 0 0
0x58080000, // 0001 LDCONST R2 K0
0x600C000C, // 0002 GETGBL R3 G12
0x5C100000, // 0003 MOVE R4 R0
0x7C0C0200, // 0004 CALL R3 1
0x140C0403, // 0005 LT R3 R2 R3
0x780E000D, // 0006 JMPF R3 #0015
0x940C0002, // 0007 GETIDX R3 R0 R2
0x8C0C0701, // 0008 GETMET R3 R3 K1
0x7C0C0200, // 0009 CALL R3 1
0x780E0007, // 000A JMPF R3 #0013
0x940C0002, // 000B GETIDX R3 R0 R2
0x880C0702, // 000C GETMBR R3 R3 K2
0x780E0000, // 000D JMPF R3 #000F
0x50040200, // 000E LDBOOL R1 1 0
0x8C0C0103, // 000F GETMET R3 R0 K3
0x5C140400, // 0010 MOVE R5 R2
0x7C0C0400, // 0011 CALL R3 2
0x70020000, // 0012 JMP #0014
0x00080504, // 0013 ADD R2 R2 K4
0x7001FFEC, // 0014 JMP #0002
0x80040200, // 0015 RET 1 R1
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: persistables
********************************************************************/
be_local_closure(Matter_Expirable_list_persistables, /* name */
be_nested_proto(
3, /* nstack */
1, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
1, /* has sup protos */
( &(const struct bproto*[ 1]) {
be_nested_proto(
2, /* nstack */
0, /* argc */
0, /* varg */
1, /* has upvals */
( &(const bupvaldesc[ 1]) { /* upvals */
be_local_const_upval(1, 1),
}),
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 1]) { /* constants */
/* K0 */ be_nested_str_weak(_persist),
}),
be_str_weak(_anonymous_),
&be_const_str_solidified,
( &(const binstruction[ 9]) { /* code */
0x50000200, // 0000 LDBOOL R0 1 0
0x78020005, // 0001 JMPF R0 #0008
0x68000000, // 0002 GETUPV R0 U0
0x7C000000, // 0003 CALL R0 0
0x88040100, // 0004 GETMBR R1 R0 K0
0x78060000, // 0005 JMPF R1 #0007
0x80040000, // 0006 RET 1 R0
0x7001FFF7, // 0007 JMP #0000
0x80000000, // 0008 RET 0
})
),
}),
1, /* has constants */
( &(const bvalue[ 1]) { /* constants */
/* K0 */ be_nested_str_weak(iter),
}),
be_str_weak(persistables),
&be_const_str_solidified,
( &(const binstruction[ 5]) { /* code */
0x8C040100, // 0000 GETMET R1 R0 K0
0x7C040200, // 0001 CALL R1 1
0x84080000, // 0002 CLOSURE R2 P0
0xA0000000, // 0003 CLOSE R0
0x80040400, // 0004 RET 1 R2
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified class: Matter_Expirable_list
********************************************************************/
extern const bclass be_class_list;
be_local_class(Matter_Expirable_list,
0,
&be_class_list,
be_nested_map(5,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_const_key_weak(every_second, 4), be_const_closure(Matter_Expirable_list_every_second_closure) },
{ be_const_key_weak(setitem, -1), be_const_closure(Matter_Expirable_list_setitem_closure) },
{ be_const_key_weak(push, -1), be_const_closure(Matter_Expirable_list_push_closure) },
{ be_const_key_weak(remove_expired, -1), be_const_closure(Matter_Expirable_list_remove_expired_closure) },
{ be_const_key_weak(persistables, -1), be_const_closure(Matter_Expirable_list_persistables_closure) },
})),
be_str_weak(Matter_Expirable_list)
);
/*******************************************************************/
void be_load_Matter_Expirable_list_class(bvm *vm) {
be_pushntvclass(vm, &be_class_Matter_Expirable_list);
be_setglobal(vm, "Matter_Expirable_list");
be_pop(vm, 1);
}
/********************************************************************/
/* End of solidification */

View File

@ -1015,7 +1015,7 @@ be_local_closure(Matter_IM_send_subscribe_update, /* name */
/* K21 */ be_nested_str_weak(suppress_response),
/* K22 */ be_nested_str_weak(send_queue),
/* K23 */ be_nested_str_weak(IM_ReportDataSubscribed),
/* K24 */ be_nested_str_weak(__message_handler),
/* K24 */ be_nested_str_weak(_message_handler),
/* K25 */ be_nested_str_weak(send_enqueued),
}),
be_str_weak(send_subscribe_update),

View File

@ -90,7 +90,7 @@ be_local_closure(Matter_IM_Subscription_init, /* name */
}),
be_str_weak(init),
&be_const_str_solidified,
( &(const binstruction[65]) { /* code */
( &(const binstruction[66]) { /* code */
0x90020001, // 0000 SETMBR R0 K0 R1
0x90020202, // 0001 SETMBR R0 K1 R2
0x90020403, // 0002 SETMBR R0 K2 R3
@ -112,50 +112,51 @@ be_local_closure(Matter_IM_Subscription_init, /* name */
0x241C0C07, // 0012 GT R7 R6 R7
0x781E0000, // 0013 JMPF R7 #0015
0x541A0E0F, // 0014 LDINT R6 3600
0x90020E06, // 0015 SETMBR R0 K7 R6
0x881C0908, // 0016 GETMBR R7 R4 K8
0x90021007, // 0017 SETMBR R0 K8 R7
0x601C0012, // 0018 GETGBL R7 G18
0x7C1C0000, // 0019 CALL R7 0
0x90021207, // 001A SETMBR R0 K9 R7
0x601C0010, // 001B GETGBL R7 G16
0x8820090A, // 001C GETMBR R8 R4 K10
0x7C1C0200, // 001D CALL R7 1
0xA802000F, // 001E EXBLK 0 #002F
0x5C200E00, // 001F MOVE R8 R7
0x7C200000, // 0020 CALL R8 0
0xB8261600, // 0021 GETNGBL R9 K11
0x8C24130C, // 0022 GETMET R9 R9 K12
0x7C240200, // 0023 CALL R9 1
0x8828110D, // 0024 GETMBR R10 R8 K13
0x90261A0A, // 0025 SETMBR R9 K13 R10
0x8828110E, // 0026 GETMBR R10 R8 K14
0x90261C0A, // 0027 SETMBR R9 K14 R10
0x8828110F, // 0028 GETMBR R10 R8 K15
0x90261E0A, // 0029 SETMBR R9 K15 R10
0x88280109, // 002A GETMBR R10 R0 K9
0x8C281510, // 002B GETMET R10 R10 K16
0x5C301200, // 002C MOVE R12 R9
0x7C280400, // 002D CALL R10 2
0x7001FFEF, // 002E JMP #001F
0x581C0011, // 002F LDCONST R7 K17
0xAC1C0200, // 0030 CATCH R7 1 0
0xB0080000, // 0031 RAISE 2 R0 R0
0x601C0012, // 0032 GETGBL R7 G18
0x7C1C0000, // 0033 CALL R7 0
0x90022407, // 0034 SETMBR R0 K18 R7
0x8C1C0113, // 0035 GETMET R7 R0 K19
0x7C1C0200, // 0036 CALL R7 1
0xB81E2800, // 0037 GETNGBL R7 K20
0x8C1C0F15, // 0038 GETMET R7 R7 K21
0xB8261600, // 0039 GETNGBL R9 K11
0x8C241317, // 003A GETMET R9 R9 K23
0x5C2C0000, // 003B MOVE R11 R0
0x7C240400, // 003C CALL R9 2
0x00262C09, // 003D ADD R9 K22 R9
0x58280018, // 003E LDCONST R10 K24
0x7C1C0600, // 003F CALL R7 3
0x80000000, // 0040 RET 0
0x541A003B, // 0015 LDINT R6 60
0x90020E06, // 0016 SETMBR R0 K7 R6
0x881C0908, // 0017 GETMBR R7 R4 K8
0x90021007, // 0018 SETMBR R0 K8 R7
0x601C0012, // 0019 GETGBL R7 G18
0x7C1C0000, // 001A CALL R7 0
0x90021207, // 001B SETMBR R0 K9 R7
0x601C0010, // 001C GETGBL R7 G16
0x8820090A, // 001D GETMBR R8 R4 K10
0x7C1C0200, // 001E CALL R7 1
0xA802000F, // 001F EXBLK 0 #0030
0x5C200E00, // 0020 MOVE R8 R7
0x7C200000, // 0021 CALL R8 0
0xB8261600, // 0022 GETNGBL R9 K11
0x8C24130C, // 0023 GETMET R9 R9 K12
0x7C240200, // 0024 CALL R9 1
0x8828110D, // 0025 GETMBR R10 R8 K13
0x90261A0A, // 0026 SETMBR R9 K13 R10
0x8828110E, // 0027 GETMBR R10 R8 K14
0x90261C0A, // 0028 SETMBR R9 K14 R10
0x8828110F, // 0029 GETMBR R10 R8 K15
0x90261E0A, // 002A SETMBR R9 K15 R10
0x88280109, // 002B GETMBR R10 R0 K9
0x8C281510, // 002C GETMET R10 R10 K16
0x5C301200, // 002D MOVE R12 R9
0x7C280400, // 002E CALL R10 2
0x7001FFEF, // 002F JMP #0020
0x581C0011, // 0030 LDCONST R7 K17
0xAC1C0200, // 0031 CATCH R7 1 0
0xB0080000, // 0032 RAISE 2 R0 R0
0x601C0012, // 0033 GETGBL R7 G18
0x7C1C0000, // 0034 CALL R7 0
0x90022407, // 0035 SETMBR R0 K18 R7
0x8C1C0113, // 0036 GETMET R7 R0 K19
0x7C1C0200, // 0037 CALL R7 1
0xB81E2800, // 0038 GETNGBL R7 K20
0x8C1C0F15, // 0039 GETMET R7 R7 K21
0xB8261600, // 003A GETNGBL R9 K11
0x8C241317, // 003B GETMET R9 R9 K23
0x5C2C0000, // 003C MOVE R11 R0
0x7C240400, // 003D CALL R9 2
0x00262C09, // 003E ADD R9 K22 R9
0x58280018, // 003F LDCONST R10 K24
0x7C1C0600, // 0040 CALL R7 3
0x80000000, // 0041 RET 0
})
)
);

View File

@ -52,7 +52,7 @@ be_local_closure(Matter_Frame_encrypt, /* name */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[29]) { /* constants */
( &(const bvalue[28]) { /* constants */
/* K0 */ be_nested_str_weak(crypto),
/* K1 */ be_nested_str_weak(raw),
/* K2 */ be_nested_str_weak(session),
@ -64,24 +64,23 @@ be_local_closure(Matter_Frame_encrypt, /* name */
/* K8 */ be_nested_str_weak(add),
/* K9 */ be_nested_str_weak(flags),
/* K10 */ be_nested_str_weak(message_counter),
/* K11 */ be_nested_str_weak(get_mode),
/* K12 */ be_nested_str_weak(__CASE),
/* K13 */ be_nested_str_weak(deviceid),
/* K14 */ be_nested_str_weak(resize),
/* K15 */ be_nested_str_weak(tasmota),
/* K16 */ be_nested_str_weak(log),
/* K17 */ be_nested_str_weak(MTR_X3A_X20cleartext_X3A_X20),
/* K18 */ be_nested_str_weak(tohex),
/* K19 */ be_nested_str_weak(MTR_X3A_X20_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A),
/* K20 */ be_nested_str_weak(MTR_X3A_X20r2i_X20_X20_X20_X20_X20_X20_X20_X20_X20_X3D),
/* K21 */ be_nested_str_weak(MTR_X3A_X20p_X20_X20_X20_X20_X20_X20_X20_X20_X20_X20_X20_X3D),
/* K22 */ be_nested_str_weak(MTR_X3A_X20a_X20_X20_X20_X20_X20_X20_X20_X20_X20_X20_X20_X3D),
/* K23 */ be_nested_str_weak(MTR_X3A_X20n_X20_X20_X20_X20_X20_X20_X20_X20_X20_X20_X20_X3D),
/* K24 */ be_nested_str_weak(AES_CCM),
/* K25 */ be_nested_str_weak(encrypt),
/* K26 */ be_nested_str_weak(tag),
/* K27 */ be_nested_str_weak(MTR_X3A_X20ciphertext_X20_X20_X3D),
/* K28 */ be_nested_str_weak(MTR_X3A_X20tag_X20_X20_X20_X20_X20_X20_X20_X20_X20_X3D),
/* K11 */ be_nested_str_weak(is_CASE),
/* K12 */ be_nested_str_weak(get_device_id),
/* K13 */ be_nested_str_weak(resize),
/* K14 */ be_nested_str_weak(tasmota),
/* K15 */ be_nested_str_weak(log),
/* K16 */ be_nested_str_weak(MTR_X3A_X20cleartext_X3A_X20),
/* K17 */ be_nested_str_weak(tohex),
/* K18 */ be_nested_str_weak(MTR_X3A_X20_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A_X2A),
/* K19 */ be_nested_str_weak(MTR_X3A_X20r2i_X20_X20_X20_X20_X20_X20_X20_X20_X20_X3D),
/* K20 */ be_nested_str_weak(MTR_X3A_X20p_X20_X20_X20_X20_X20_X20_X20_X20_X20_X20_X20_X3D),
/* K21 */ be_nested_str_weak(MTR_X3A_X20a_X20_X20_X20_X20_X20_X20_X20_X20_X20_X20_X20_X3D),
/* K22 */ be_nested_str_weak(MTR_X3A_X20n_X20_X20_X20_X20_X20_X20_X20_X20_X20_X20_X20_X3D),
/* K23 */ be_nested_str_weak(AES_CCM),
/* K24 */ be_nested_str_weak(encrypt),
/* K25 */ be_nested_str_weak(tag),
/* K26 */ be_nested_str_weak(MTR_X3A_X20ciphertext_X20_X20_X3D),
/* K27 */ be_nested_str_weak(MTR_X3A_X20tag_X20_X20_X20_X20_X20_X20_X20_X20_X20_X3D),
}),
be_str_weak(encrypt),
&be_const_str_solidified,
@ -110,58 +109,58 @@ be_local_closure(Matter_Frame_encrypt, /* name */
0x7C200600, // 0015 CALL R8 3
0x8C20070B, // 0016 GETMET R8 R3 K11
0x7C200200, // 0017 CALL R8 1
0x8824070C, // 0018 GETMBR R9 R3 K12
0x1C201009, // 0019 EQ R8 R8 R9
0x78220003, // 001A JMPF R8 #001F
0x8820070D, // 001B GETMBR R8 R3 K13
0x78220001, // 001C JMPF R8 #001F
0x8820070D, // 001D GETMBR R8 R3 K13
0x78220005, // 0018 JMPF R8 #001F
0x8C20070C, // 0019 GETMET R8 R3 K12
0x7C200200, // 001A CALL R8 1
0x78220002, // 001B JMPF R8 #001F
0x8C20070C, // 001C GETMET R8 R3 K12
0x7C200200, // 001D CALL R8 1
0x40200E08, // 001E CONNECT R8 R7 R8
0x8C200F0E, // 001F GETMET R8 R7 K14
0x8C200F0D, // 001F GETMET R8 R7 K13
0x542A000C, // 0020 LDINT R10 13
0x7C200400, // 0021 CALL R8 2
0xB8221E00, // 0022 GETNGBL R8 K15
0x8C201110, // 0023 GETMET R8 R8 K16
0xB8221C00, // 0022 GETNGBL R8 K14
0x8C20110F, // 0023 GETMET R8 R8 K15
0x88280101, // 0024 GETMBR R10 R0 K1
0x8C281512, // 0025 GETMET R10 R10 K18
0x8C281511, // 0025 GETMET R10 R10 K17
0x7C280200, // 0026 CALL R10 1
0x002A220A, // 0027 ADD R10 K17 R10
0x002A200A, // 0027 ADD R10 K16 R10
0x542E0003, // 0028 LDINT R11 4
0x7C200600, // 0029 CALL R8 3
0xB8221E00, // 002A GETNGBL R8 K15
0x8C201110, // 002B GETMET R8 R8 K16
0x58280013, // 002C LDCONST R10 K19
0xB8221C00, // 002A GETNGBL R8 K14
0x8C20110F, // 002B GETMET R8 R8 K15
0x58280012, // 002C LDCONST R10 K18
0x542E0003, // 002D LDINT R11 4
0x7C200600, // 002E CALL R8 3
0xB8221E00, // 002F GETNGBL R8 K15
0x8C201110, // 0030 GETMET R8 R8 K16
0x8C280912, // 0031 GETMET R10 R4 K18
0xB8221C00, // 002F GETNGBL R8 K14
0x8C20110F, // 0030 GETMET R8 R8 K15
0x8C280911, // 0031 GETMET R10 R4 K17
0x7C280200, // 0032 CALL R10 1
0x002A280A, // 0033 ADD R10 K20 R10
0x002A260A, // 0033 ADD R10 K19 R10
0x542E0003, // 0034 LDINT R11 4
0x7C200600, // 0035 CALL R8 3
0xB8221E00, // 0036 GETNGBL R8 K15
0x8C201110, // 0037 GETMET R8 R8 K16
0x8C280D12, // 0038 GETMET R10 R6 K18
0xB8221C00, // 0036 GETNGBL R8 K14
0x8C20110F, // 0037 GETMET R8 R8 K15
0x8C280D11, // 0038 GETMET R10 R6 K17
0x7C280200, // 0039 CALL R10 1
0x002A2A0A, // 003A ADD R10 K21 R10
0x002A280A, // 003A ADD R10 K20 R10
0x542E0003, // 003B LDINT R11 4
0x7C200600, // 003C CALL R8 3
0xB8221E00, // 003D GETNGBL R8 K15
0x8C201110, // 003E GETMET R8 R8 K16
0x8C280B12, // 003F GETMET R10 R5 K18
0xB8221C00, // 003D GETNGBL R8 K14
0x8C20110F, // 003E GETMET R8 R8 K15
0x8C280B11, // 003F GETMET R10 R5 K17
0x7C280200, // 0040 CALL R10 1
0x002A2C0A, // 0041 ADD R10 K22 R10
0x002A2A0A, // 0041 ADD R10 K21 R10
0x542E0003, // 0042 LDINT R11 4
0x7C200600, // 0043 CALL R8 3
0xB8221E00, // 0044 GETNGBL R8 K15
0x8C201110, // 0045 GETMET R8 R8 K16
0x8C280F12, // 0046 GETMET R10 R7 K18
0xB8221C00, // 0044 GETNGBL R8 K14
0x8C20110F, // 0045 GETMET R8 R8 K15
0x8C280F11, // 0046 GETMET R10 R7 K17
0x7C280200, // 0047 CALL R10 1
0x002A2E0A, // 0048 ADD R10 K23 R10
0x002A2C0A, // 0048 ADD R10 K22 R10
0x542E0003, // 0049 LDINT R11 4
0x7C200600, // 004A CALL R8 3
0x8C200318, // 004B GETMET R8 R1 K24
0x8C200317, // 004B GETMET R8 R1 K23
0x5C280800, // 004C MOVE R10 R4
0x5C2C0E00, // 004D MOVE R11 R7
0x5C300A00, // 004E MOVE R12 R5
@ -170,37 +169,37 @@ be_local_closure(Matter_Frame_encrypt, /* name */
0x7C340200, // 0051 CALL R13 1
0x543A000F, // 0052 LDINT R14 16
0x7C200C00, // 0053 CALL R8 6
0x8C241119, // 0054 GETMET R9 R8 K25
0x8C241118, // 0054 GETMET R9 R8 K24
0x5C2C0C00, // 0055 MOVE R11 R6
0x7C240400, // 0056 CALL R9 2
0x8C28111A, // 0057 GETMET R10 R8 K26
0x8C281119, // 0057 GETMET R10 R8 K25
0x7C280200, // 0058 CALL R10 1
0xB82E1E00, // 0059 GETNGBL R11 K15
0x8C2C1710, // 005A GETMET R11 R11 K16
0x58340013, // 005B LDCONST R13 K19
0xB82E1C00, // 0059 GETNGBL R11 K14
0x8C2C170F, // 005A GETMET R11 R11 K15
0x58340012, // 005B LDCONST R13 K18
0x543A0003, // 005C LDINT R14 4
0x7C2C0600, // 005D CALL R11 3
0xB82E1E00, // 005E GETNGBL R11 K15
0x8C2C1710, // 005F GETMET R11 R11 K16
0x8C341312, // 0060 GETMET R13 R9 K18
0xB82E1C00, // 005E GETNGBL R11 K14
0x8C2C170F, // 005F GETMET R11 R11 K15
0x8C341311, // 0060 GETMET R13 R9 K17
0x7C340200, // 0061 CALL R13 1
0x0036360D, // 0062 ADD R13 K27 R13
0x0036340D, // 0062 ADD R13 K26 R13
0x543A0003, // 0063 LDINT R14 4
0x7C2C0600, // 0064 CALL R11 3
0xB82E1E00, // 0065 GETNGBL R11 K15
0x8C2C1710, // 0066 GETMET R11 R11 K16
0x8C341512, // 0067 GETMET R13 R10 K18
0xB82E1C00, // 0065 GETNGBL R11 K14
0x8C2C170F, // 0066 GETMET R11 R11 K15
0x8C341511, // 0067 GETMET R13 R10 K17
0x7C340200, // 0068 CALL R13 1
0x0036380D, // 0069 ADD R13 K28 R13
0x0036360D, // 0069 ADD R13 K27 R13
0x543A0003, // 006A LDINT R14 4
0x7C2C0600, // 006B CALL R11 3
0xB82E1E00, // 006C GETNGBL R11 K15
0x8C2C1710, // 006D GETMET R11 R11 K16
0x58340013, // 006E LDCONST R13 K19
0xB82E1C00, // 006C GETNGBL R11 K14
0x8C2C170F, // 006D GETMET R11 R11 K15
0x58340012, // 006E LDCONST R13 K18
0x543A0003, // 006F LDINT R14 4
0x7C2C0600, // 0070 CALL R11 3
0x882C0101, // 0071 GETMBR R11 R0 K1
0x8C2C170E, // 0072 GETMET R11 R11 K14
0x8C2C170D, // 0072 GETMET R11 R11 K13
0x88340105, // 0073 GETMBR R13 R0 K5
0x7C2C0400, // 0074 CALL R11 2
0x882C0101, // 0075 GETMBR R11 R0 K1
@ -405,7 +404,7 @@ be_local_closure(Matter_Frame_build_response, /* name */
/* K13 */ be_nested_str_weak(message_counter),
/* K14 */ be_nested_str_weak(counter_snd),
/* K15 */ be_nested_str_weak(next),
/* K16 */ be_nested_str_weak(_counter_insecure_snd),
/* K16 */ be_nested_str_weak(__counter_insecure_snd),
/* K17 */ be_nested_str_weak(x_flag_i),
/* K18 */ be_nested_str_weak(opcode),
/* K19 */ be_nested_str_weak(exchange_id),
@ -541,9 +540,9 @@ be_local_closure(Matter_Frame_initiate_response, /* name */
/* K2 */ be_nested_str_weak(matter),
/* K3 */ be_nested_str_weak(Frame),
/* K4 */ be_nested_str_weak(remote_ip),
/* K5 */ be_nested_str_weak(__ip),
/* K5 */ be_nested_str_weak(_ip),
/* K6 */ be_nested_str_weak(remote_port),
/* K7 */ be_nested_str_weak(__port),
/* K7 */ be_nested_str_weak(_port),
/* K8 */ be_nested_str_weak(flag_dsiz),
/* K9 */ be_const_int(0),
/* K10 */ be_nested_str_weak(session),
@ -552,11 +551,11 @@ be_local_closure(Matter_Frame_initiate_response, /* name */
/* K13 */ be_nested_str_weak(counter_snd),
/* K14 */ be_nested_str_weak(next),
/* K15 */ be_nested_str_weak(local_session_id),
/* K16 */ be_nested_str_weak(_counter_insecure_snd),
/* K16 */ be_nested_str_weak(__counter_insecure_snd),
/* K17 */ be_nested_str_weak(x_flag_i),
/* K18 */ be_const_int(1),
/* K19 */ be_nested_str_weak(opcode),
/* K20 */ be_nested_str_weak(__exchange_id),
/* K20 */ be_nested_str_weak(_exchange_id),
/* K21 */ be_nested_str_weak(exchange_id),
/* K22 */ be_nested_str_weak(protocol_id),
/* K23 */ be_nested_str_weak(x_flag_r),

View File

@ -211,9 +211,9 @@ be_local_closure(Matter_MessageHandler_msg_received, /* name */
/* K15 */ be_nested_str_weak(MTR_X3A_X20find_X20session_X20by_X20source_node_id_X20_X3D_X20),
/* K16 */ be_nested_str_weak(session_id_X20_X3D_X20),
/* K17 */ be_const_int(3),
/* K18 */ be_nested_str_weak(__ip),
/* K19 */ be_nested_str_weak(__port),
/* K20 */ be_nested_str_weak(__message_handler),
/* K18 */ be_nested_str_weak(_ip),
/* K19 */ be_nested_str_weak(_port),
/* K20 */ be_nested_str_weak(_message_handler),
/* K21 */ be_nested_str_weak(session),
/* K22 */ be_nested_str_weak(counter_rcv),
/* K23 */ be_nested_str_weak(validate),

View File

@ -332,7 +332,7 @@ be_local_closure(Matter_UDPServer_init, /* name */
0x90020403, // 0009 SETMBR R0 K2 R3
0x500C0000, // 000A LDBOOL R3 0 0
0x90020603, // 000B SETMBR R0 K3 R3
0x600C0013, // 000C GETGBL R3 G19
0x600C0012, // 000C GETGBL R3 G18
0x7C0C0000, // 000D CALL R3 0
0x90020803, // 000E SETMBR R0 K4 R3
0x80000000, // 000F RET 0
@ -356,91 +356,93 @@ be_local_closure(Matter_UDPServer_resend_packets, /* name */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[23]) { /* constants */
/* K0 */ be_nested_str_weak(packets_sent),
/* K1 */ be_nested_str_weak(tasmota),
/* K2 */ be_nested_str_weak(time_reached),
/* K3 */ be_nested_str_weak(next_try),
/* K4 */ be_nested_str_weak(retries),
/* K5 */ be_nested_str_weak(RETRIES),
/* K6 */ be_nested_str_weak(log),
/* K7 */ be_nested_str_weak(MTR_X3A_X20resending_X20packet_X20id_X3D),
/* K8 */ be_nested_str_weak(msg_id),
/* K9 */ be_const_int(3),
/* K10 */ be_nested_str_weak(send),
/* K11 */ be_nested_str_weak(udp_socket),
/* K12 */ be_nested_str_weak(millis),
/* K13 */ be_nested_str_weak(backoff_time),
/* K14 */ be_const_int(1),
/* K15 */ be_nested_str_weak(string),
/* K16 */ be_nested_str_weak(remove),
/* K17 */ be_nested_str_weak(format),
/* K18 */ be_nested_str_weak(MTR_X3A_X20target_X20unreachable_X20_X27_X5B_X25s_X5D_X3A_X25i_X27_X20msg_id_X3D_X25i),
/* K19 */ be_nested_str_weak(addr),
/* K20 */ be_nested_str_weak(port),
/* K21 */ be_const_int(2),
/* K22 */ be_nested_str_weak(stop_iteration),
/* K0 */ be_const_int(0),
/* K1 */ be_nested_str_weak(packets_sent),
/* K2 */ be_nested_str_weak(tasmota),
/* K3 */ be_nested_str_weak(time_reached),
/* K4 */ be_nested_str_weak(next_try),
/* K5 */ be_nested_str_weak(retries),
/* K6 */ be_nested_str_weak(RETRIES),
/* K7 */ be_nested_str_weak(log),
/* K8 */ be_nested_str_weak(MTR_X3A_X20resending_X20packet_X20id_X3D),
/* K9 */ be_nested_str_weak(msg_id),
/* K10 */ be_const_int(3),
/* K11 */ be_nested_str_weak(send),
/* K12 */ be_nested_str_weak(udp_socket),
/* K13 */ be_nested_str_weak(millis),
/* K14 */ be_nested_str_weak(backoff_time),
/* K15 */ be_const_int(1),
/* K16 */ be_nested_str_weak(string),
/* K17 */ be_nested_str_weak(remove),
/* K18 */ be_nested_str_weak(format),
/* K19 */ be_nested_str_weak(MTR_X3A_X20target_X20unreachable_X20_X27_X5B_X25s_X5D_X3A_X25i_X27_X20msg_id_X3D_X25i),
/* K20 */ be_nested_str_weak(addr),
/* K21 */ be_nested_str_weak(port),
/* K22 */ be_const_int(2),
}),
be_str_weak(resend_packets),
&be_const_str_solidified,
( &(const binstruction[58]) { /* code */
0x60040010, // 0000 GETGBL R1 G16
0x88080100, // 0001 GETMBR R2 R0 K0
0x7C040200, // 0002 CALL R1 1
0xA8020031, // 0003 EXBLK 0 #0036
0x5C080200, // 0004 MOVE R2 R1
0x7C080000, // 0005 CALL R2 0
0xB80E0200, // 0006 GETNGBL R3 K1
0x8C0C0702, // 0007 GETMET R3 R3 K2
0x88140503, // 0008 GETMBR R5 R2 K3
0x7C0C0400, // 0009 CALL R3 2
0x780E0029, // 000A JMPF R3 #0035
0x880C0504, // 000B GETMBR R3 R2 K4
0x88100105, // 000C GETMBR R4 R0 K5
0x180C0604, // 000D LE R3 R3 R4
0x780E0016, // 000E JMPF R3 #0026
0xB80E0200, // 000F GETNGBL R3 K1
0x8C0C0706, // 0010 GETMET R3 R3 K6
0x60140008, // 0011 GETGBL R5 G8
0x88180508, // 0012 GETMBR R6 R2 K8
0x7C140200, // 0013 CALL R5 1
0x00160E05, // 0014 ADD R5 K7 R5
0x58180009, // 0015 LDCONST R6 K9
0x7C0C0600, // 0016 CALL R3 3
0x8C0C050A, // 0017 GETMET R3 R2 K10
0x8814010B, // 0018 GETMBR R5 R0 K11
0x7C0C0400, // 0019 CALL R3 2
0xB80E0200, // 001A GETNGBL R3 K1
0x8C0C070C, // 001B GETMET R3 R3 K12
0x7C0C0200, // 001C CALL R3 1
0x8C10050D, // 001D GETMET R4 R2 K13
0x88180504, // 001E GETMBR R6 R2 K4
0x7C100400, // 001F CALL R4 2
0x000C0604, // 0020 ADD R3 R3 R4
0x900A0603, // 0021 SETMBR R2 K3 R3
0x880C0504, // 0022 GETMBR R3 R2 K4
0x000C070E, // 0023 ADD R3 R3 K14
0x900A0803, // 0024 SETMBR R2 K4 R3
0x7002000E, // 0025 JMP #0035
0xA40E1E00, // 0026 IMPORT R3 K15
0x88100100, // 0027 GETMBR R4 R0 K0
0x8C100910, // 0028 GETMET R4 R4 K16
0x88180508, // 0029 GETMBR R6 R2 K8
0x7C100400, // 002A CALL R4 2
0xB8120200, // 002B GETNGBL R4 K1
0x8C100906, // 002C GETMET R4 R4 K6
0x8C180711, // 002D GETMET R6 R3 K17
0x58200012, // 002E LDCONST R8 K18
0x88240513, // 002F GETMBR R9 R2 K19
0x88280514, // 0030 GETMBR R10 R2 K20
0x882C0508, // 0031 GETMBR R11 R2 K8
0x7C180A00, // 0032 CALL R6 5
0x581C0015, // 0033 LDCONST R7 K21
0x7C100600, // 0034 CALL R4 3
0x7001FFCD, // 0035 JMP #0004
0x58040016, // 0036 LDCONST R1 K22
0xAC040200, // 0037 CATCH R1 1 0
0xB0080000, // 0038 RAISE 2 R0 R0
0x80000000, // 0039 RET 0
( &(const binstruction[60]) { /* code */
0x58040000, // 0000 LDCONST R1 K0
0x6008000C, // 0001 GETGBL R2 G12
0x880C0101, // 0002 GETMBR R3 R0 K1
0x7C080200, // 0003 CALL R2 1
0x14080202, // 0004 LT R2 R1 R2
0x780A0034, // 0005 JMPF R2 #003B
0x88080101, // 0006 GETMBR R2 R0 K1
0x94080401, // 0007 GETIDX R2 R2 R1
0xB80E0400, // 0008 GETNGBL R3 K2
0x8C0C0703, // 0009 GETMET R3 R3 K3
0x88140504, // 000A GETMBR R5 R2 K4
0x7C0C0400, // 000B CALL R3 2
0x780E002B, // 000C JMPF R3 #0039
0x880C0505, // 000D GETMBR R3 R2 K5
0x88100106, // 000E GETMBR R4 R0 K6
0x180C0604, // 000F LE R3 R3 R4
0x780E0017, // 0010 JMPF R3 #0029
0xB80E0400, // 0011 GETNGBL R3 K2
0x8C0C0707, // 0012 GETMET R3 R3 K7
0x60140008, // 0013 GETGBL R5 G8
0x88180509, // 0014 GETMBR R6 R2 K9
0x7C140200, // 0015 CALL R5 1
0x00161005, // 0016 ADD R5 K8 R5
0x5818000A, // 0017 LDCONST R6 K10
0x7C0C0600, // 0018 CALL R3 3
0x8C0C050B, // 0019 GETMET R3 R2 K11
0x8814010C, // 001A GETMBR R5 R0 K12
0x7C0C0400, // 001B CALL R3 2
0xB80E0400, // 001C GETNGBL R3 K2
0x8C0C070D, // 001D GETMET R3 R3 K13
0x7C0C0200, // 001E CALL R3 1
0x8C10050E, // 001F GETMET R4 R2 K14
0x88180505, // 0020 GETMBR R6 R2 K5
0x7C100400, // 0021 CALL R4 2
0x000C0604, // 0022 ADD R3 R3 R4
0x900A0803, // 0023 SETMBR R2 K4 R3
0x880C0505, // 0024 GETMBR R3 R2 K5
0x000C070F, // 0025 ADD R3 R3 K15
0x900A0A03, // 0026 SETMBR R2 K5 R3
0x0004030F, // 0027 ADD R1 R1 K15
0x7002000E, // 0028 JMP #0038
0xA40E2000, // 0029 IMPORT R3 K16
0x88100101, // 002A GETMBR R4 R0 K1
0x8C100911, // 002B GETMET R4 R4 K17
0x5C180200, // 002C MOVE R6 R1
0x7C100400, // 002D CALL R4 2
0xB8120400, // 002E GETNGBL R4 K2
0x8C100907, // 002F GETMET R4 R4 K7
0x8C180712, // 0030 GETMET R6 R3 K18
0x58200013, // 0031 LDCONST R8 K19
0x88240514, // 0032 GETMBR R9 R2 K20
0x88280515, // 0033 GETMBR R10 R2 K21
0x882C0509, // 0034 GETMBR R11 R2 K9
0x7C180A00, // 0035 CALL R6 5
0x581C0016, // 0036 LDCONST R7 K22
0x7C100600, // 0037 CALL R4 3
0x70020000, // 0038 JMP #003A
0x0004030F, // 0039 ADD R1 R1 K15
0x7001FFC5, // 003A JMP #0001
0x80000000, // 003B RET 0
})
)
);
@ -452,7 +454,7 @@ be_local_closure(Matter_UDPServer_resend_packets, /* name */
********************************************************************/
be_local_closure(Matter_UDPServer_packet_ack, /* name */
be_nested_proto(
6, /* nstack */
7, /* nstack */
2, /* argc */
2, /* varg */
0, /* has upvals */
@ -460,39 +462,50 @@ be_local_closure(Matter_UDPServer_packet_ack, /* name */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 6]) { /* constants */
/* K0 */ be_nested_str_weak(packets_sent),
/* K1 */ be_nested_str_weak(contains),
/* K2 */ be_nested_str_weak(remove),
/* K3 */ be_nested_str_weak(tasmota),
/* K4 */ be_nested_str_weak(log),
/* K5 */ be_nested_str_weak(MTR_X3A_X20removed_X20packet_X20from_X20sending_X20list_X20id_X3D),
( &(const bvalue[ 8]) { /* constants */
/* K0 */ be_const_int(0),
/* K1 */ be_nested_str_weak(packets_sent),
/* K2 */ be_nested_str_weak(msg_id),
/* K3 */ be_nested_str_weak(remove),
/* K4 */ be_nested_str_weak(tasmota),
/* K5 */ be_nested_str_weak(log),
/* K6 */ be_nested_str_weak(MTR_X3A_X20removed_X20packet_X20from_X20sending_X20list_X20id_X3D),
/* K7 */ be_const_int(1),
}),
be_str_weak(packet_ack),
&be_const_str_solidified,
( &(const binstruction[22]) { /* code */
( &(const binstruction[31]) { /* code */
0x4C080000, // 0000 LDNIL R2
0x1C080202, // 0001 EQ R2 R1 R2
0x780A0000, // 0002 JMPF R2 #0004
0x80000400, // 0003 RET 0
0x88080100, // 0004 GETMBR R2 R0 K0
0x8C080501, // 0005 GETMET R2 R2 K1
0x5C100200, // 0006 MOVE R4 R1
0x7C080400, // 0007 CALL R2 2
0x780A000B, // 0008 JMPF R2 #0015
0x88080100, // 0009 GETMBR R2 R0 K0
0x8C080502, // 000A GETMET R2 R2 K2
0x5C100200, // 000B MOVE R4 R1
0x7C080400, // 000C CALL R2 2
0xB80A0600, // 000D GETNGBL R2 K3
0x8C080504, // 000E GETMET R2 R2 K4
0x60100008, // 000F GETGBL R4 G8
0x5C140200, // 0010 MOVE R5 R1
0x7C100200, // 0011 CALL R4 1
0x00120A04, // 0012 ADD R4 K5 R4
0x54160003, // 0013 LDINT R5 4
0x7C080600, // 0014 CALL R2 3
0x80000000, // 0015 RET 0
0x58080000, // 0004 LDCONST R2 K0
0x600C000C, // 0005 GETGBL R3 G12
0x88100101, // 0006 GETMBR R4 R0 K1
0x7C0C0200, // 0007 CALL R3 1
0x140C0403, // 0008 LT R3 R2 R3
0x780E0013, // 0009 JMPF R3 #001E
0x880C0101, // 000A GETMBR R3 R0 K1
0x940C0602, // 000B GETIDX R3 R3 R2
0x880C0702, // 000C GETMBR R3 R3 K2
0x1C0C0601, // 000D EQ R3 R3 R1
0x780E000C, // 000E JMPF R3 #001C
0x880C0101, // 000F GETMBR R3 R0 K1
0x8C0C0703, // 0010 GETMET R3 R3 K3
0x5C140400, // 0011 MOVE R5 R2
0x7C0C0400, // 0012 CALL R3 2
0xB80E0800, // 0013 GETNGBL R3 K4
0x8C0C0705, // 0014 GETMET R3 R3 K5
0x60140008, // 0015 GETGBL R5 G8
0x5C180200, // 0016 MOVE R6 R1
0x7C140200, // 0017 CALL R5 1
0x00160C05, // 0018 ADD R5 K6 R5
0x541A0003, // 0019 LDINT R6 4
0x7C0C0600, // 001A CALL R3 3
0x70020000, // 001B JMP #001D
0x00080507, // 001C ADD R2 R2 K7
0x7001FFE6, // 001D JMP #0005
0x80000000, // 001E RET 0
})
)
);
@ -662,16 +675,17 @@ be_local_closure(Matter_UDPServer_send_response, /* name */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 5]) { /* constants */
( &(const bvalue[ 6]) { /* constants */
/* K0 */ be_nested_str_weak(matter),
/* K1 */ be_nested_str_weak(UDPPacket_sent),
/* K2 */ be_nested_str_weak(send),
/* K3 */ be_nested_str_weak(udp_socket),
/* K4 */ be_nested_str_weak(packets_sent),
/* K5 */ be_nested_str_weak(push),
}),
be_str_weak(send_response),
&be_const_str_solidified,
( &(const binstruction[14]) { /* code */
( &(const binstruction[16]) { /* code */
0xB8160000, // 0000 GETNGBL R5 K0
0x8C140B01, // 0001 GETMET R5 R5 K1
0x5C1C0200, // 0002 MOVE R7 R1
@ -682,10 +696,12 @@ be_local_closure(Matter_UDPServer_send_response, /* name */
0x8C180B02, // 0007 GETMET R6 R5 K2
0x88200103, // 0008 GETMBR R8 R0 K3
0x7C180400, // 0009 CALL R6 2
0x78120001, // 000A JMPF R4 #000D
0x78120003, // 000A JMPF R4 #000F
0x88180104, // 000B GETMBR R6 R0 K4
0x98180805, // 000C SETIDX R6 R4 R5
0x80000000, // 000D RET 0
0x8C180D05, // 000C GETMET R6 R6 K5
0x5C200A00, // 000D MOVE R8 R5
0x7C180400, // 000E CALL R6 2
0x80000000, // 000F RET 0
})
)
);

File diff suppressed because it is too large Load Diff