Skip to content

Commit

Permalink
Matter add Fan support (virtual only) (#21637)
Browse files Browse the repository at this point in the history
* Matter add Fan support (virtual only)

* Add MtrReceived event
  • Loading branch information
s-hadinger committed Jun 15, 2024
1 parent 767ac60 commit 2cf0eda
Show file tree
Hide file tree
Showing 16 changed files with 890 additions and 54 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
- Extend command ``SetOption147 1`` to disable publish of IRReceived MQTT messages (#21574)
- Matter support for Rain sensor (#21633)
- Matter add internal debug option
- Matter add Fan support (virtual only)

### Breaking Changed

Expand Down
2 changes: 2 additions & 0 deletions lib/libesp32/berry_matter/src/be_matter_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ extern const bclass be_class_Matter_TLV; // need to declare it upfront because
#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_9_Virt_Fan.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"
Expand Down
3 changes: 2 additions & 1 deletion lib/libesp32/berry_matter/src/embedded/Matter_Plugin_0.be
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Matter_Plugin.be - generic superclass for all Matter plugins, used to define specific behaviors (light, switch, media...)
# Matter_Plugin_0.be - generic superclass for all Matter plugins, used to define specific behaviors (light, switch, media...)
#
# Copyright (C) 2023 Stephan Hadinger & Theo Arends
#
Expand Down Expand Up @@ -48,6 +48,7 @@ class Matter_Plugin
static var FEATURE_MAPS = { # feature map per cluster
0x0031: 0x04, # Put Eth for now which should work for any on-network
0x0102: 1 + 4, # Lift + PA_LF
0x0202: 2, # Fan: Auto
}
# `CLUSTER_REVISIONS` contains revision numbers for each cluster, or `1` if not present
static var CLUSTER_REVISIONS = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Matter_Plugin_Aggregator.be - implements the Aggregator endpoint
# Matter_Plugin_1_Aggregator.be - implements the Aggregator endpoint
#
# Copyright (C) 2023 Stephan Hadinger & Theo Arends
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Matter_Plugin_Device.be - implements the behavior for a standard Device
# Matter_Plugin_1_Device.be - implements the behavior for a standard Device
#
# Copyright (C) 2023 Stephan Hadinger & Theo Arends
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Matter_Plugin_Root.be - implements the core features that a Matter device must implemment
# Matter_Plugin_1_Root.be - implements the core features that a Matter device must implemment
#
# Copyright (C) 2023 Stephan Hadinger & Theo Arends
#
Expand Down
198 changes: 198 additions & 0 deletions lib/libesp32/berry_matter/src/embedded/Matter_Plugin_2_Fan.be
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
#
# Matter_Plugin_2_Fan.be - implements the behavior for a generic Fan
#
# 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

# Matter plug-in for core behavior

#@ solidify:Matter_Plugin_Fan,weak

class Matter_Plugin_Fan : Matter_Plugin_Device
static var TYPE = "fan" # name of the plug-in in json
static var DISPLAY_NAME = "Fan" # display name of the plug-in
# static var ARG = "" # additional argument name (or empty if none)
static var CLUSTERS = matter.consolidate_clusters(_class, {
# 0x001D: inherited # Descriptor Cluster 9.5 p.453
# 0x0003: inherited # Identify 1.2 p.16
# 0x0004: inherited # Groups 1.3 p.21
# 0x0005: inherited # Scenes 1.4 p.30 - no writable
0x0202: [0,1,2,3], # Fan
})
static var UPDATE_COMMANDS = matter.UC_LIST(_class, "FanMode", "FanSpeed")
static var TYPES = { 0x002B: 2 } # Fan

# Inherited
# var device # reference to the `device` global object
# var endpoint # current endpoint
# var clusters # map from cluster to list of attributes, typically constructed from CLUSTERS hierachy
# var tick # tick value when it was last updated
# var node_label # name of the endpoint, used only in bridge mode, "" if none
var shadow_fan_mode
var shadow_fan_speed_pct
#############################################################
# FanMode:
# 0: Off
# 1: Low
# 2: Medium
# 3: High
# 4: On -- deprecated
# 5: Auto -- not declared as supported
# 6: Smart -- deprecated

#############################################################
# Constructor
def init(device, endpoint, config)
super(self).init(device, endpoint, config)
self.shadow_fan_mode = 0 # Off by default
self.shadow_fan_speed_pct = 0
end

#############################################################
# Model
#
def set_fan_mode(fan_mode)
fan_mode = int(fan_mode)
if (fan_mode < 0) fan_mode = 0 end # force positive
if fan_mode != self.shadow_fan_mode
self.attribute_updated(0x0202, 0x0000)
self.shadow_fan_mode = int(fan_mode)
# compute new speed
var new_speed_pct = self.shadow_fan_speed_pct
if self.shadow_fan_mode == 0 # set to Off, we need to adjust speed to 0 (4.4.6.1.1)
new_speed_pct = 0
elif self.shadow_fan_mode > 3 # Auto mode or unsupported modes, since we don't support AUTO, set speed to max
self.shadow_fan_mode = 3 # HIGH
new_speed_pct = 100
else # set to value
new_speed_pct = tasmota.scale_uint(fan_mode, 0, 3, 0, 100)
end
# adjust and advertize if speed changed
if self.shadow_fan_speed_pct != new_speed_pct
self.shadow_fan_speed_pct = new_speed_pct
self.attribute_updated(0x0202, 0x0002)
end
end
end

def set_fan_speed_pct(fan_speed_pct)
# guard value
fan_speed_pct = int(fan_speed_pct)
if (fan_speed_pct < 0) fan_speed_pct = 0 end
if (fan_speed_pct > 100) fan_speed_pct = 100 end
if fan_speed_pct != self.shadow_fan_speed_pct
self.attribute_updated(0x0202, 0x0002)
self.shadow_fan_speed_pct = fan_speed_pct
# adjust mode if needed
var new_mode = self.shadow_fan_mode
if (fan_speed_pct == 0)
new_mode = 0
else
new_mode = tasmota.scale_uint(fan_speed_pct, 1, 100, 1, 3)
end
# adjust and advertize if mode changed
if (new_mode != self.shadow_fan_mode)
self.shadow_fan_mode = new_mode
self.attribute_updated(0x0202, 0x0000)
end
end
end

#############################################################
# read an attribute
#
def read_attribute(session, ctx, tlv_solo)
var TLV = matter.TLV
var cluster = ctx.cluster
var attribute = ctx.attribute

# ====================================================================================================
if cluster == 0x0202 # ========== Fan ==========
self.update_shadow_lazy()
if attribute == 0x0000 # ---------- FanMode / enum8 ----------
return tlv_solo.set(TLV.U1, self.shadow_fan_mode)
elif attribute == 0x0001 # ---------- FanModeSequence / enum8 ----------
return tlv_solo.set(TLV.U1, 2) # Off/Low/Med/High/Auto
elif attribute == 0x0002 # ---------- PercentSetting / enum8 ----------
return tlv_solo.set(TLV.U1, self.shadow_fan_speed_pct)
elif attribute == 0x0003 # ---------- PercentSetting / enum8 ----------
return tlv_solo.set(TLV.U1, self.shadow_fan_speed_pct)
end

end
return super(self).read_attribute(session, ctx, tlv_solo)
end

#############################################################
# MVC Model
#
# Controller write attributes
#############################################################
#############################################################
# write attribute
def write_attribute(session, ctx, write_data)
var TLV = matter.TLV
var cluster = ctx.cluster
var attribute = ctx.attribute

# ====================================================================================================
if cluster == 0x0202 # ========== Fan ==========
self.update_shadow_lazy()
if attribute == 0x0000 # ---------- FanMode / enum8 ----------
if type(write_data) == 'int'
self.set_fan_mode(write_data)
self.publish_command('FanMode', self.shadow_fan_mode, 'FanSpeed', self.shadow_fan_speed_pct)
return true
else
ctx.status = matter.CONSTRAINT_ERROR
return false
end
elif attribute == 0x0002 # ---------- PercentSetting / enum8 ----------
if type(write_data) == 'int'
self.set_fan_speed_pct(write_data)
self.publish_command('FanMode', self.shadow_fan_mode, 'FanSpeed', self.shadow_fan_speed_pct)
return true
else
ctx.status = matter.CONSTRAINT_ERROR
return false
end
end

end
# return super(self).read_attribute(session, ctx, tlv_solo) # not useful as there is nothing in superclass
return nil
end

#############################################################
# update_virtual
#
# Update internal state for virtual devices
def update_virtual(payload)
var val_fan_mode = payload.find("FanMode")
if val_fan_mode != nil
self.set_fan_mode(int(val_fan_mode))
end
var val_fan_speed = payload.find("FanSpeed")
if val_fan_speed != nil
self.set_fan_speed_pct(int(val_fan_speed))
end
# super(self).update_virtual(payload) # not useful as there is nothing in superclass
end

end
matter.Plugin_Fan = Matter_Plugin_Fan
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Matter_Plugin_3_Sensor_Waterleak.be - implements the behavior for a Water leak Sensor
# Matter_Plugin_2_Sensor_Waterleak.be - implements the behavior for a Water leak Sensor
#
# Copyright (C) 2024 Stephan Hadinger & Theo Arends
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Matter_Plugin_Bridge_8_Sensor_Rain.be - implements Rain Sensor via HTTP to Tasmota
# Matter_Plugin_Bridge_Sensor_Rain.be - implements Rain Sensor via HTTP to Tasmota
#
# Copyright (C) 2024 Stephan Hadinger & Theo Arends
#
Expand Down
33 changes: 33 additions & 0 deletions lib/libesp32/berry_matter/src/embedded/Matter_Plugin_9_Virt_Fan.be
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#
# Matter_Plugin_Virt_Fan.be - implements the behavior for a Virtual Fan
#
# 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

# Matter plug-in for core behavior

#@ solidify:Matter_Plugin_Virt_Fan,weak

class Matter_Plugin_Virt_Fan : Matter_Plugin_Fan
static var TYPE = "v_fan" # name of the plug-in in json
static var DISPLAY_NAME = "v.Fan" # display name of the plug-in
static var ARG = "" # no arg for virtual device
static var ARG_HINT = "_Not used_" # Hint for entering the Argument (inside 'placeholder')
static var VIRTUAL = true # virtual device
end
matter.Plugin_Virt_Fan = Matter_Plugin_Virt_Fan
1 change: 1 addition & 0 deletions lib/libesp32/berry_matter/src/embedded/Matter_UI.be
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class Matter_UI
"|temperature|pressure|illuminance|humidity|occupancy|onoff|contact|flow|rain|waterleak"
"|airquality"
"|-virtual|v_relay|v_light0|v_light1|v_light2|v_light3"
"|v_fan"
"|v_temp|v_pressure|v_illuminance|v_humidity|v_occupancy|v_contact|v_flow|v_rain|v_waterleak"
"|v_airquality"
static var _CLASSES_TYPES2= "|http_relay|http_light0|http_light1|http_light2|http_light3"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1359,10 +1359,11 @@ be_local_class(Matter_Plugin,
be_nested_map(51,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_const_key_weak(FEATURE_MAPS, -1), be_const_simple_instance(be_nested_simple_instance(&be_class_map, {
be_const_map( * be_nested_map(2,
be_const_map( * be_nested_map(3,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_const_key_int(258, -1), be_const_int(5) },
{ be_const_key_int(49, -1), be_const_int(4) },
{ be_const_key_int(49, 2), be_const_int(4) },
{ be_const_key_int(514, -1), be_const_int(2) },
})) ) } )) },
{ be_const_key_weak(append_state_json, -1), be_const_closure(class_Matter_Plugin_append_state_json_closure) },
{ be_const_key_weak(parse_configuration, -1), be_const_closure(class_Matter_Plugin_parse_configuration_closure) },
Expand Down
Loading

0 comments on commit 2cf0eda

Please sign in to comment.