mirror of https://github.com/arendst/Tasmota.git
Matter refactor CASE sessions and QRCode (#18196)
This commit is contained in:
parent
4ff9cf4e59
commit
1131ffada1
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
||||
-#
|
|
@ -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
|
||||
|
||||
#############################################################
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
#############################################################
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,82 +217,103 @@ 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)
|
||||
var _i2r_privacy # cache for the i2r privacy key
|
||||
var attestation_challenge # Attestation challenge
|
||||
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
|
||||
var shared_secret # ECDH shared secret used in CASE
|
||||
# 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)
|
||||
end
|
||||
return self._fabric.get_ca_pub()
|
||||
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
|
||||
|
@ -285,29 +459,27 @@ class Matter_Session
|
|||
var v = introspect.get(self, k)
|
||||
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
|
||||
if k == "counter_rcv" v = v.val()
|
||||
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())
|
||||
end
|
||||
var fabs = []
|
||||
for f : self.fabrics.persistables()
|
||||
for _ : f._sessions.persistables() sessions_saved += 1 end # count persitable sessions
|
||||
fabs.push(f.tojson())
|
||||
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
|
||||
if session != nil
|
||||
self.add_session(session)
|
||||
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
|
||||
|
||||
self.fabrics.push(fabric)
|
||||
end
|
||||
|
||||
tasmota.log(string.format("MTR: Loaded %i session(s)", size(self.sessions)), 2)
|
||||
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
|
||||
|
|
|
@ -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)
|
||||
tasmota.log("MTR: removed packet from sending list id=" + str(id), 4)
|
||||
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
|
||||
|
||||
|
|
|
@ -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> Matter Passcode </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> [ Commissioning open for %i min ] </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> Matter Passcode </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,36 +170,34 @@ 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> Sessions </b></legend><p></p>")
|
||||
webserver.content_send("<p>Existing sessions:</p>")
|
||||
webserver.content_send("<fieldset><legend><b> Fabrics </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> Session %i </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()
|
||||
webserver.content_send(string.format("Fabric: %s<br>", fabric_rev.tohex()))
|
||||
webserver.content_send(string.format("Device: %s<br> ", deviceid_rev.tohex()))
|
||||
var first = true
|
||||
for f : self.device.sessions.fabrics.persistables()
|
||||
if !first webserver.content_send("<hr>") end
|
||||
first = false
|
||||
|
||||
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("<fieldset><legend><b> %s </b></legend><p></p>", "<No label>"))
|
||||
|
||||
webserver.content_send("<p></p></fieldset><p></p>")
|
||||
end
|
||||
i += 1
|
||||
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> ", 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_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
|
||||
end
|
||||
|
||||
|
@ -146,17 +205,6 @@ class Matter_UI
|
|||
|
||||
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
|
@ -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
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
})
|
||||
)
|
||||
);
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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
Loading…
Reference in New Issue