Files
Tasmota/lib/libesp32/berry_matter/src/be_matter_module.c
T
5acc4a368b Add Matter virtual IR HVAC thermostat support (#24821)
* Add Matter virtual IR HVAC thermostat support

Introduce a generic Matter thermostat plugin and a virtual HVAC plugin that maps thermostat state changes to Tasmota IRHVAC commands.

Add virtual HVAC option switches for IRHVAC boolean options such as Econo, Quiet, Turbo, Light, Filter, Clean, Beep, and iFeel. These option endpoints are attached to the HVAC endpoint PartsList instead of being listed as top-level aggregator parts.

Expose the new virtual device types in the Matter UI and refresh endpoint PartsList attributes when endpoint composition changes.

* Warn on invalid Matter v.HVAC vendor

* Fix regression

* Matter virtual IR HVAC thermostat support

* Revert

---------

Co-authored-by: Jean-Laurent Girod <macjl@users.noreply.github.com>
Co-authored-by: s-hadinger <49731213+s-hadinger@users.noreply.github.com>
2026-07-07 20:00:24 +02:00

467 lines
19 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
be_matter_module.c - implements the high level `matter` Berry module
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/>.
*/
/********************************************************************
* Matter global module
*
*******************************************************************/
#include "be_constobj.h"
#include "be_mapping.h"
#include <stdio.h>
extern uint32_t matter_convert_seconds_to_dhm(uint32_t seconds, char *unit, uint32_t *color, bbool days);
char* matter_seconds_to_dhm(int32_t seconds) {
static const char empty_resp[] = "<td>&nbsp;</td>";
static char res[64]; // static to allow returning to Berry
char unit;
uint32_t color; // color of text
if (seconds < 0) { return empty_resp; } // no value
uint32_t val = matter_convert_seconds_to_dhm(seconds, &unit, &color, bfalse);
if (val < 100) {
snprintf(res, sizeof(res), "<td style=\"color:#%02x%02x%02x\">&#x1F557;%02d%c</td>",
(color & 0xFF0000) >> 16, (color & 0x00FF00) >> 8, (color & 0x0000FF),
val, unit);
} else {
return empty_resp;
}
return res;
}
BE_FUNC_CTYPE_DECLARE(matter_seconds_to_dhm, "s", "i")
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"
#include "../generate/be_matter_clusters.h"
#include "../generate/be_matter_events.h"
#include "../generate/be_matter_opcodes.h"
#include "../generate/be_matter_vendors.h"
const char* matter_get_vendor_name(uint16_t id) {
for (const matter_vendor_t * vnd = matter_Vendors; vnd->id != 0xFFFF; vnd++) {
if (vnd->id == id) {
return vnd->name;
}
}
return NULL;
}
BE_FUNC_CTYPE_DECLARE(matter_get_vendor_name, "s", "i")
const char* matter_get_cluster_name(uint16_t cluster) {
for (const matter_cluster_t * cl = matterAllClusters; cl->id != 0xFFFF; cl++) {
if (cl->id == cluster) {
return cl->name;
}
}
return NULL;
}
BE_FUNC_CTYPE_DECLARE(matter_get_cluster_name, "s", "i")
const char* matter_get_opcode_name(uint16_t opcode) {
for (const matter_opcode_t * op = matter_OpCodes; op->id != 0xFFFF; op++) {
if (op->id == opcode) {
return op->name;
}
}
return NULL;
}
BE_FUNC_CTYPE_DECLARE(matter_get_opcode_name, "s", "i")
const char* matter_get_attribute_name(uint16_t cluster, uint16_t attribute) {
if (attribute < 0xFFF0) {
for (const matter_cluster_t * cl = matterAllClusters; cl->id != 0xFFFF; cl++) {
if (cl->id == cluster) {
for (const matter_attribute_t * at = cl->attributes; at->id != 0xFFFF; at++) {
if (at->id == attribute) {
return at->name;
}
}
}
}
} else {
for (const matter_attribute_t * at = matter_Attributes_Common; at->id != 0xFFFF; at++) {
if (at->id == attribute) {
return at->name;
}
}
}
return NULL;
}
BE_FUNC_CTYPE_DECLARE(matter_get_attribute_name, "s", "ii")
bbool matter_is_attribute_writable(uint16_t cluster, uint16_t attribute) {
if (attribute < 0xFFF0) {
for (const matter_cluster_t * cl = matterAllClusters; cl->id != 0xFFFF; cl++) {
if (cl->id == cluster) {
for (const matter_attribute_t * at = cl->attributes; at->id != 0xFFFF; at++) {
if (at->id == attribute) {
return (at->flags & 0x01) ? btrue : bfalse;
}
}
}
}
}
return bfalse;
}
BE_FUNC_CTYPE_DECLARE(matter_is_attribute_writable, "b", "ii")
bbool matter_is_attribute_reportable(uint16_t cluster, uint16_t attribute) {
if (attribute < 0xFFF0) {
for (const matter_cluster_t * cl = matterAllClusters; cl->id != 0xFFFF; cl++) {
if (cl->id == cluster) {
for (const matter_attribute_t * at = cl->attributes; at->id != 0xFFFF; at++) {
if (at->id == attribute) {
return (at->flags & 0x02) ? btrue : bfalse;
}
}
}
}
} else {
return btrue;
}
return bfalse;
}
BE_FUNC_CTYPE_DECLARE(matter_is_attribute_reportable, "b", "ii")
const char* matter_get_command_name(uint16_t cluster, uint16_t command) {
for (const matter_cluster_t * cl = matterAllClusters; cl->id != 0xFFFF; cl++) {
if (cl->id == cluster) {
for (const matter_command_t * cmd = cl->commands; cmd->id != 0xFFFF; cmd++) {
if (cmd->id == command) {
return cmd->name;
}
}
}
}
return NULL;
}
BE_FUNC_CTYPE_DECLARE(matter_get_command_name, "s", "ii")
const char* matter_get_event_name(uint16_t cluster, uint8_t event) {
for (const matter_event_t * ev = matter_Events; ev->cluster != 0xFFFF; ev++) {
if (ev->cluster == cluster && ev->event == event) {
return ev->name;
}
}
return NULL;
}
BE_FUNC_CTYPE_DECLARE(matter_get_event_name, "s", "ii")
// Convert an IP address from string to raw bytes
extern const void* matter_get_ip_bytes(const char* ip_str, size_t* ret_len);
BE_FUNC_CTYPE_DECLARE(matter_get_ip_bytes, "&", "s")
extern int matter_publish_command(bvm *vm);
#include "solidify/solidified_Matter_0_Inspect.h"
extern const bclass be_class_Matter_TLV; // need to declare it upfront because of circular reference
#include "solidify/solidified_Matter_Path_0.h"
#include "solidify/solidified_Matter_Path_1_PathGenerator.h"
#include "solidify/solidified_Matter_Path_1_EventGenerator.h"
#include "solidify/solidified_Matter_Certs.h"
#include "solidify/solidified_Matter_TLV.h"
#include "solidify/solidified_Matter_IM_Data.h"
#include "solidify/solidified_Matter_UDPServer.h"
#include "solidify/solidified_Matter_TCP_async.h"
#include "solidify/solidified_Matter_HTTP_async.h"
#include "solidify/solidified_Matter_HTTP_remote.h"
#include "solidify/solidified_Matter_Expirable.h"
#include "solidify/solidified_Matter_Fabric.h"
#include "solidify/solidified_Matter_Session.h"
#include "solidify/solidified_Matter_Session_Store.h"
#include "solidify/solidified_Matter_Commissioning_Data.h"
#include "solidify/solidified_Matter_Commissioning_Context.h"
#include "solidify/solidified_Matter_Message.h"
#include "solidify/solidified_Matter_MessageHandler.h"
#include "solidify/solidified_Matter_IM_Message.h"
#include "solidify/solidified_Matter_IM_Subscription.h"
#include "solidify/solidified_Matter_IM.h"
#include "solidify/solidified_Matter_EventHandler.h"
#include "solidify/solidified_Matter_Control_Message.h"
#include "solidify/solidified_Matter_Plugin_0.h"
#include "solidify/solidified_Matter_z_Commissioning.h"
#include "solidify/solidified_Matter_z_Autoconf.h"
#include "solidify/solidified_Matter_z_Zigbee.h"
#include "solidify/solidified_Matter_Base38.h"
#include "solidify/solidified_Matter_UI.h"
#include "solidify/solidified_Matter_Profiler.h"
#include "solidify/solidified_Matter_Plugin_1_Root.h"
#include "solidify/solidified_Matter_Plugin_1_Aggregator.h"
#include "solidify/solidified_Matter_Plugin_1_Device.h"
#include "solidify/solidified_Matter_Plugin_3_OnOff.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_OnOff.h"
#include "solidify/solidified_Matter_Plugin_2_Sensor_Air_Quality.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_Sensor_Air_Quality.h"
#include "solidify/solidified_Matter_Plugin_2_Light0.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_Light0.h"
#include "solidify/solidified_Matter_Plugin_3_Light1.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_Light1.h"
#include "solidify/solidified_Matter_Plugin_4_Light2.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_Light2.h"
#include "solidify/solidified_Matter_Plugin_4_Light3.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_Light3.h"
#include "solidify/solidified_Matter_Plugin_2_Shutter.h"
#include "solidify/solidified_Matter_Plugin_3_ShutterTilt.h"
#include "solidify/solidified_Matter_Plugin_2_Sensor.h"
#include "solidify/solidified_Matter_Plugin_3_Sensor_Pressure.h"
#include "solidify/solidified_Matter_Plugin_3_Sensor_Flow.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_Sensor_Pressure.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_Sensor_Flow.h"
#include "solidify/solidified_Matter_Plugin_3_Sensor_Temp.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_Sensor_Temp.h"
#include "solidify/solidified_Matter_Plugin_3_Sensor_Illuminance.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_Sensor_Illuminance.h"
#include "solidify/solidified_Matter_Plugin_3_Sensor_Humidity.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_Sensor_Humidity.h"
#include "solidify/solidified_Matter_Plugin_2_Sensor_Boolean.h"
#include "solidify/solidified_Matter_Plugin_3_Sensor_Occupancy.h"
#include "solidify/solidified_Matter_Plugin_3_Sensor_OnOff.h"
#include "solidify/solidified_Matter_Plugin_3_Sensor_Contact.h"
#include "solidify/solidified_Matter_Plugin_3_Sensor_Rain.h"
#include "solidify/solidified_Matter_Plugin_3_Sensor_Waterleak.h"
#include "solidify/solidified_Matter_Plugin_2_Fan.h"
#include "solidify/solidified_Matter_Plugin_2_Thermostat.h"
#include "solidify/solidified_Matter_Plugin_2_Sensor_GenericSwitch_Btn.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_Fan.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_HVAC.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_HVAC_Option.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_Sensor_Contact.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_Sensor_Occupancy.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_Sensor_Rain.h"
#include "solidify/solidified_Matter_Plugin_9_Virt_Sensor_Waterleak.h"
#include "solidify/solidified_Matter_Plugin_8_Bridge_OnOff.h"
#include "solidify/solidified_Matter_Plugin_8_Bridge_Light0.h"
#include "solidify/solidified_Matter_Plugin_8_Bridge_Light1.h"
#include "solidify/solidified_Matter_Plugin_8_Bridge_Light2.h"
#include "solidify/solidified_Matter_Plugin_8_Bridge_Light3.h"
#include "solidify/solidified_Matter_Plugin_8_Bridge_Sensor_Pressure.h"
#include "solidify/solidified_Matter_Plugin_8_Bridge_Sensor_Temp.h"
#include "solidify/solidified_Matter_Plugin_8_Bridge_Sensor_Illuminance.h"
#include "solidify/solidified_Matter_Plugin_8_Bridge_Sensor_Humidity.h"
#include "solidify/solidified_Matter_Plugin_8_Bridge_Sensor_Occupancy.h"
#include "solidify/solidified_Matter_Plugin_8_Bridge_Sensor_Contact.h"
#include "solidify/solidified_Matter_Plugin_8_Bridge_Sensor_Flow.h"
#include "solidify/solidified_Matter_Plugin_8_Bridge_Sensor_Air_Quality.h"
#include "solidify/solidified_Matter_Plugin_8_Bridge_Sensor_Rain.h"
#include "solidify/solidified_Matter_Plugin_8_Bridge_Sensor_Waterleak.h"
#include "solidify/solidified_Matter_Plugin_9_Zigbee_Light0.h"
#include "solidify/solidified_Matter_Plugin_9_Zigbee_Light1.h"
#include "solidify/solidified_Matter_Plugin_9_Zigbee_Light2.h"
#include "solidify/solidified_Matter_Plugin_9_Zigbee_Temperature.h"
#include "solidify/solidified_Matter_Plugin_9_Zigbee_Pressure.h"
#include "solidify/solidified_Matter_Plugin_9_Zigbee_Humidity.h"
#include "solidify/solidified_Matter_Plugin_9_Zigbee_Occupancy.h"
#include "solidify/solidified_Matter_Plugin_z_All.h"
#if USE_MI_EXT_GUI
#include "solidify/solidified_Matter_BTP.h"
#include "solidify/solidified_Matter_zz_Device_BLE.h"
// #warning ________________
#endif //USE_MI_EXT_GUI
#include "solidify/solidified_Matter_zz_Device.h"
#include "be_fixed_matter.h"
/* @const_object_info_begin
module matter (scope: global, strings: weak) {
// MATTER_OPTION, int(151) // SetOption151 enables Matter
// AGGREGATOR_ENDPOINT, int(0x0001) // some controllers require aggregator to be endpoint 1
// START_ENDPOINT, int(0x0002) // endpoint where to start devices
seconds_to_dhm, ctype_func(matter_seconds_to_dhm)
Verhoeff, class(be_class_Matter_Verhoeff)
Counter, class(be_class_Matter_Counter)
setmember, closure(module_matter_setmember_closure)
member, closure(module_matter_member_closure)
get_ip_bytes, ctype_func(matter_get_ip_bytes)
publish_command, func(matter_publish_command)
get_vendor_name, ctype_func(matter_get_vendor_name)
get_cluster_name, ctype_func(matter_get_cluster_name)
get_attribute_name, ctype_func(matter_get_attribute_name)
is_attribute_writable, ctype_func(matter_is_attribute_writable)
is_attribute_reportable, ctype_func(matter_is_attribute_reportable)
get_command_name, ctype_func(matter_get_command_name)
get_event_name, ctype_func(matter_get_event_name)
get_opcode_name, ctype_func(matter_get_opcode_name)
TLV, class(be_class_Matter_TLV)
sort, closure(module_matter_sort_closure)
jitter, closure(module_matter_jitter_closure)
inspect, closure(module_matter_inspect_closure)
consolidate_clusters, closure(module_matter_consolidate_clusters_closure)
UC_LIST, closure(module_matter_UC_LIST_closure)
Profiler, class(be_class_Matter_Profiler)
// EVents priority levels
// EVENT_DEBUG, int(0)
// EVENT_INFO, int(1)
// EVENT_CRITICAL, int(2)
// Status codes
// SUCCESS, int(0x00)
// FAILURE, int(0x01)
// INVALID_SUBSCRIPTION, int(0x7D)
// UNSUPPORTED_ACCESS, int(0x7E)
// UNSUPPORTED_ENDPOINT, int(0x7F)
// INVALID_ACTION, int(0x80)
// UNSUPPORTED_COMMAND, int(0x81)
// INVALID_COMMAND, int(0x85)
// UNSUPPORTED_ATTRIBUTE, int(0x86)
// CONSTRAINT_ERROR, int(0x87)
// UNSUPPORTED_WRITE, int(0x88)
// RESOURCE_EXHAUSTED, int(0x89)
// NOT_FOUND, int(0x8B)
// UNREPORTABLE_ATTRIBUTE, int(0x8C)
// INVALID_DATA_TYPE, int(0x8D)
// UNSUPPORTED_READ, int(0x8F)
// DATA_VERSION_MISMATCH, int(0x92)
// TIMEOUT, int(0x94)
// UNSUPPORTED_NODE, int(0x9B)
// BUSY, int(0x9C)
// UNSUPPORTED_CLUSTER, int(0xC3)
// NO_UPSTREAM_SUBSCRIP­TION, int(0xC5)
// NEEDS_TIMED_INTERACTION, int(0xC6)
// UNSUPPORTED_EVENT, int(0xC7)
// PATHS_EXHAUSTED, int(0xC8)
// TIMED_REQUEST_MISMATCH, int(0xC9)
// FAILSAFE_REQUIRED, int(0xCA)
// Matter_Data_IM classes
AttributePathIB, class(be_class_Matter_AttributePathIB)
ClusterPathIB, class(be_class_Matter_ClusterPathIB)
DataVersionFilterIB, class(be_class_Matter_DataVersionFilterIB)
AttributeDataIB, class(be_class_Matter_AttributeDataIB)
AttributeReportIB, class(be_class_Matter_AttributeReportIB)
EventFilterIB, class(be_class_Matter_EventFilterIB)
EventPathIB, class(be_class_Matter_EventPathIB)
EventDataIB, class(be_class_Matter_EventDataIB)
EventReportIB, class(be_class_Matter_EventReportIB)
CommandPathIB, class(be_class_Matter_CommandPathIB)
CommandDataIB, class(be_class_Matter_CommandDataIB)
InvokeResponseIB, class(be_class_Matter_InvokeResponseIB)
CommandStatusIB, class(be_class_Matter_CommandStatusIB)
EventStatusIB, class(be_class_Matter_EventStatusIB)
AttributeStatusIB, class(be_class_Matter_AttributeStatusIB)
StatusIB, class(be_class_Matter_StatusIB)
StatusResponseMessage, class(be_class_Matter_StatusResponseMessage)
ReadRequestMessage, class(be_class_Matter_ReadRequestMessage)
ReadRequestMessage_solo, class(be_class_Matter_ReadRequestMessage_solo)
ReportDataMessage, class(be_class_Matter_ReportDataMessage)
SubscribeRequestMessage, class(be_class_Matter_SubscribeRequestMessage)
SubscribeResponseMessage, class(be_class_Matter_SubscribeResponseMessage)
WriteRequestMessage, class(be_class_Matter_WriteRequestMessage)
WriteResponseMessage, class(be_class_Matter_WriteResponseMessage)
TimedRequestMessage, class(be_class_Matter_TimedRequestMessage)
InvokeRequestMessage, class(be_class_Matter_InvokeRequestMessage)
InvokeRequestMessage_solo, class(be_class_Matter_InvokeRequestMessage_solo)
InvokeResponseMessage, class(be_class_Matter_InvokeResponseMessage)
// Matter Commisioning messages
PBKDFParamRequest, class(be_class_Matter_PBKDFParamRequest)
PBKDFParamResponse, class(be_class_Matter_PBKDFParamResponse)
Pake1, class(be_class_Matter_Pake1)
Pake2, class(be_class_Matter_Pake2)
Pake3, class(be_class_Matter_Pake3)
Sigma1, class(be_class_Matter_Sigma1)
Sigma2, class(be_class_Matter_Sigma2)
Sigma2Resume, class(be_class_Matter_Sigma2Resume)
Sigma3, class(be_class_Matter_Sigma3)
Commisioning_Context, class(be_class_Matter_Commisioning_Context)
// UDP Server
UDPPacket_sent, class(be_class_Matter_UDPPacket_sent)
UDPServer, class(be_class_Matter_UDPServer)
TCP_async, class(be_class_Matter_TCP_async)
HTTP_async, class(be_class_Matter_HTTP_async)
HTTP_remote, class(be_class_Matter_HTTP_remote)
// 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)
// Message Handler
Frame, class(be_class_Matter_Frame)
MessageHandler, class(be_class_Matter_MessageHandler)
// Event Handler
EventHandler, class(be_class_Matter_EventHandler)
EventQueued, class(be_class_Matter_EventQueued)
// Interation Model
Path, class(be_class_Matter_Path)
PathGenerator, class(be_class_Matter_PathGenerator)
EventGenerator, class(be_class_Matter_EventGenerator)
IM_Status, class(be_class_Matter_IM_Status)
IM_InvokeResponse, class(be_class_Matter_IM_InvokeResponse)
IM_WriteResponse, class(be_class_Matter_IM_WriteResponse)
IM_ReportData_Pull, class(be_class_Matter_IM_ReportData_Pull)
IM_ReportDataSubscribed_Pull, class(be_class_Matter_IM_ReportDataSubscribed_Pull)
IM_SubscribeResponse_Pull, class(be_class_Matter_IM_SubscribeResponse_Pull)
IM_SubscribedHeartbeat, class(be_class_Matter_IM_SubscribedHeartbeat)
IM_Subscription, class(be_class_Matter_IM_Subscription)
IM_Subscription_Shop, class(be_class_Matter_IM_Subscription_Shop)
IM, class(be_class_Matter_IM)
Control_Message, class(be_class_Matter_Control_Message)
UI, class(be_class_Matter_UI)
// Commissioning
Commissioning, class(be_class_Matter_Commissioning)
Autoconf, class(be_class_Matter_Autoconf)
// QR Code
QRCode, class(be_class_Matter_QRCode)
// Base38 for QR Code
Base38, class(be_class_Matter_Base38)
// optional BTP for BLE
BTP, class(be_class_Matter_BTP), USE_MI_EXT_GUI
// optional Matter BLE Device core class
Device_BLE, class(be_class_Matter_Device_BLE), USE_MI_EXT_GUI
// Matter Device core class
Device, class(be_class_Matter_Device)
// credentials from example
Certs, class(be_class_Matter_Certs)
// Plugins - only the core classes, all others are taken from `matter_device.plugins_classes`
Plugin_Root, class(be_class_Matter_Plugin_Root) // Generic behavior common to all devices
Plugin_Aggregator, class(be_class_Matter_Plugin_Aggregator) // Aggregator
Plugin_Device, class(be_class_Matter_Plugin_Device) // Device
}
@const_object_info_end */