Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Matter redesigned UI #18855

Merged
merged 1 commit into from
Jun 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
### Added
- Matter ability to add or remove endpoint in bridge mode (code only)
- Matter add controller's Vendor Name to logs and UI
- Matter redesigned UI

### Breaking Changed

Expand Down
99 changes: 87 additions & 12 deletions lib/libesp32/berry_matter/src/embedded/Matter_Device.be
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class Matter_Device
var root_discriminator # as `int`
var root_passcode # as `int`
var ipv4only # advertize only IPv4 addresses (no IPv6)
var next_ep # next endpoint to be allocated for bridge, start at 51
var next_ep # next endpoint to be allocated for bridge, start at 1
# context for PBKDF
var root_iterations # PBKDF number of iterations
# PBKDF information used only during PASE (freed afterwards)
Expand All @@ -88,7 +88,7 @@ class Matter_Device
self.vendorid = self.VENDOR_ID
self.productid = self.PRODUCT_ID
self.root_iterations = self.PBKDF_ITERATIONS
self.next_ep = 51 # start at endpoint 51 for dynamically allocated endpoints
self.next_ep = 1 # start at endpoint 1 for dynamically allocated endpoints
self.root_salt = crypto.random(16)
self.ipv4only = false
self.load_param()
Expand Down Expand Up @@ -605,6 +605,20 @@ class Matter_Device
return ret
end

#############################################################
# Find plugin by endpoint
def find_plugin_by_endpoint(ep)
var idx = 0
while idx < size(self.plugins)
var pl = self.plugins[idx]
if pl.get_endpoint() == ep
return pl
end
idx += 1
end
return nil
end

#############################################################
# Persistance of Matter Device parameters
#
Expand Down Expand Up @@ -1090,13 +1104,30 @@ class Matter_Device
# auto-detect sensors
var sensors = json.load(tasmota.read_sensors())

var sensors_list = self.autoconf_sensors_list(sensors)

for s: sensors_list
m[str(endpoint)] = s
endpoint += 1
end

# tasmota.publish_result('{"Matter":{"Initialized":1}}', 'Matter') # MQTT is not yet connected
return m
end


#############################################################
# Autoconfigure from sensors
#
# Returns an ordered list
def autoconf_sensors_list(sensors)
var ret = []
# temperature sensors
for k1:self.k2l(sensors)
var sensor_2 = sensors[k1]
if isinstance(sensor_2, map) && sensor_2.contains("Temperature")
var temp_rule = k1 + "#Temperature"
m[str(endpoint)] = {'type':'temperature','filter':temp_rule}
endpoint += 1
ret.push({'type':'temperature','filter':temp_rule})
end
end

Expand All @@ -1105,8 +1136,7 @@ class Matter_Device
var sensor_2 = sensors[k1]
if isinstance(sensor_2, map) && sensor_2.contains("Pressure")
var temp_rule = k1 + "#Pressure"
m[str(endpoint)] = {'type':'pressure','filter':temp_rule}
endpoint += 1
ret.push({'type':'pressure','filter':temp_rule})
end
end

Expand All @@ -1115,8 +1145,7 @@ class Matter_Device
var sensor_2 = sensors[k1]
if isinstance(sensor_2, map) && sensor_2.contains("Illuminance")
var temp_rule = k1 + "#Illuminance"
m[str(endpoint)] = {'type':'illuminance','filter':temp_rule}
endpoint += 1
ret.push({'type':'illuminance','filter':temp_rule})
end
end

Expand All @@ -1125,12 +1154,11 @@ class Matter_Device
var sensor_2 = sensors[k1]
if isinstance(sensor_2, map) && sensor_2.contains("Humidity")
var temp_rule = k1 + "#Humidity"
m[str(endpoint)] = {'type':'humidity','filter':temp_rule}
endpoint += 1
ret.push({'type':'humidity','filter':temp_rule})
end
end
# tasmota.publish_result('{"Matter":{"Initialized":1}}', 'Matter') # MQTT is not yet connected
return m

return ret
end

# get keys of a map in sorted order
Expand Down Expand Up @@ -1262,6 +1290,8 @@ class Matter_Device
idx += 1
end
end
# clean any orphan remote
self.clean_remotes()
end

#############################################################
Expand Down Expand Up @@ -1337,6 +1367,51 @@ class Matter_Device
return http_remote
end

#####################################################################
# Remove HTTP remotes that are no longer referenced
def clean_remotes()
import introspect
import string

var remotes_map = {} # key: remote object, value: count of references

# init all remotes with count 0
for http_remote: self.http_remotes
remotes_map[http_remote] = 0
end

# scan all endpoints
for pi: self.plugins
var http_remote = introspect.get(pi, "http_remote")
if http_remote != nil
remotes_map[http_remote] = remotes_map.find(http_remote, 0) + 1
end
end

tasmota.log("MTR: remotes references: " + str(remotes_map))

for remote:remotes_map.keys()
if remotes_map[remote] == 0
# remove
tasmota.log("MTR: remove unused remote: " + remote.addr, 3)
remote.close()
self.http_remotes.remove(remote)
end
end

end

# def get_remotes_list()
#####################################################################
# Get sorted list of remote endpoints
# def get_remotes_list()
# var ret = []
# for hr: self.http_remotes
# ret.push(hr.addr)
# end
# return self.sort_distinct(ret)
# end

#####################################################################
# Commands `Mtr___`
#####################################################################
Expand Down
20 changes: 19 additions & 1 deletion lib/libesp32/berry_matter/src/embedded/Matter_Plugin.be
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,28 @@ class Matter_Plugin
# device: contains the root device object so the plugin can "call home"
# endpoint: (int) the endpoint number (16 bits)
# arguments: (map) the map for all complementary arguments that are plugin specific
def init(device, endpoint, arguments)
def init(device, endpoint, config)
self.device = device
self.endpoint = endpoint
self.clusters = self.consolidate_clusters()
self.parse_configuration(config)
end

#############################################################
# parse_configuration
#
# Parse configuration map
# TO BE OVERRIDEN
def parse_configuration(config)
end

#############################################################
# is_local_device
#
# Returns true if it's a local device, or false for a
# remotely device controlled via HTTP
def is_local_device()
return true
end

#############################################################
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ class Matter_Plugin_Bridge_HTTP : Matter_Plugin_Device
self.register_cmd_cb()
end

#############################################################
# is_local_device
#
# Returns true if it's a local device, or false for a
# remotely device controlled via HTTP
def is_local_device()
return false
end

#############################################################
# register_cmd_cb
#
Expand Down Expand Up @@ -196,34 +205,6 @@ class Matter_Plugin_Bridge_HTTP : Matter_Plugin_Device
# avoid calling update_shadow() since it's not applicable for HTTP remote
end

#############################################################
# UI Methods
#############################################################
# ui_conf_to_string
#
# Convert the current plugin parameters to a single string
static def ui_conf_to_string(cl, conf)
var s = super(_class).ui_conf_to_string(cl, conf)

var url = str(conf.find(_class.ARG_HTTP, ''))
var arg = s + "," + url
# print("MTR: ui_conf_to_string", conf, cl, arg)
return arg
end

#############################################################
# ui_string_to_conf
#
# Convert the string in UI to actual parameters added to the map
static def ui_string_to_conf(cl, conf, arg)
import string
var elts = string.split(arg + ',', ',', 3) # add ',' at the end to be sure to have at least 2 arguments
conf[_class.ARG_HTTP] = elts[1]
super(_class).ui_string_to_conf(cl, conf, elts[0])
# print("ui_string_to_conf", conf, arg)
return conf
end

#############################################################
# web_values
#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Matter_Plugin_Bridge_HTTP end

class Matter_Plugin_Bridge_Light0 : Matter_Plugin_Bridge_HTTP
static var TYPE = "http_light0" # name of the plug-in in json
static var NAME = "&#x1F517; Light 0 On" # display name of the plug-in
static var NAME = "Light 0 On" # display name of the plug-in
static var ARG = "relay" # additional argument name (or empty if none)
static var ARG_TYPE = / x -> int(x) # function to convert argument to the right type
# static var UPDATE_TIME = 3000 # update every 3s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Matter_Plugin_Bridge_Light0 end

class Matter_Plugin_Bridge_Light1 : Matter_Plugin_Bridge_Light0
static var TYPE = "http_light1" # name of the plug-in in json
static var NAME = "&#x1F517; Light 1 Dimmer" # display name of the plug-in
static var NAME = "Light 1 Dimmer" # display name of the plug-in
# static var ARG = "relay" # additional argument name (or empty if none)
# static var ARG_TYPE = / x -> int(x) # function to convert argument to the right type
static var CLUSTERS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Matter_Plugin_Bridge_Light1 end

class Matter_Plugin_Bridge_Light2 : Matter_Plugin_Bridge_Light1
static var TYPE = "http_light2" # name of the plug-in in json
static var NAME = "&#x1F517; Light 2 CT" # display name of the plug-in
static var NAME = "Light 2 CT" # display name of the plug-in
# static var ARG = "relay" # additional argument name (or empty if none)
# static var ARG_TYPE = / x -> int(x) # function to convert argument to the right type
static var CLUSTERS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Matter_Plugin_Bridge_Light1 end

class Matter_Plugin_Bridge_Light3 : Matter_Plugin_Bridge_Light1
static var TYPE = "http_light3" # name of the plug-in in json
static var NAME = "&#x1F517; Light 3 RGB" # display name of the plug-in
static var NAME = "Light 3 RGB" # display name of the plug-in
# static var ARG = "relay" # additional argument name (or empty if none)
# static var ARG_TYPE = / x -> int(x) # function to convert argument to the right type
static var CLUSTERS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Matter_Plugin_Bridge_Light0 end

class Matter_Plugin_Bridge_OnOff : Matter_Plugin_Bridge_Light0
static var TYPE = "http_relay" # name of the plug-in in json
static var NAME = "&#x1F517; Relay" # display name of the plug-in
static var NAME = "Relay" # display name of the plug-in
static var TYPES = { 0x010A: 2 } # On/Off Plug-in Unit

#############################################################
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ class Matter_Plugin_Bridge_Sensor : Matter_Plugin_Bridge_HTTP
var shadow_value # Last known value

#############################################################
# Constructor
def init(device, endpoint, arguments)
super(self).init(device, endpoint, arguments)
self.tasmota_sensor_filter = arguments.find(self.ARG#-'filter'-#)
# parse_configuration
#
# Parse configuration map
def parse_configuration(config)
self.tasmota_sensor_filter = config.find(self.ARG#-'filter'-#)
if self.tasmota_sensor_filter
self.tasmota_sensor_matcher = tasmota.Rule_Matcher.parse(self.tasmota_sensor_filter)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Matter_Plugin_Bridge_Sensor end

class Matter_Plugin_Bridge_Sensor_Humidity : Matter_Plugin_Bridge_Sensor
static var TYPE = "http_humidity" # name of the plug-in in json
static var NAME = "&#x1F517; Humidity" # display name of the plug-in
static var NAME = "Humidity" # display name of the plug-in

static var CLUSTERS = {
0x0405: [0,1,2,0xFFFC,0xFFFD], # Humidity Measurement p.102 - no writable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Matter_Plugin_Bridge_Sensor end

class Matter_Plugin_Bridge_Sensor_Illuminance : Matter_Plugin_Bridge_Sensor
static var TYPE = "http_illuminance" # name of the plug-in in json
static var NAME = "&#x1F517; Illuminance" # display name of the plug-in
static var NAME = "Illuminance" # display name of the plug-in

static var CLUSTERS = {
0x0400: [0,1,2,0xFFFC,0xFFFD], # Illuminance Measurement p.95 - no writable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Matter_Plugin_Bridge_HTTP end

class Matter_Plugin_Bridge_Sensor_Occupancy : Matter_Plugin_Bridge_HTTP
static var TYPE = "http_occupancy" # name of the plug-in in json
static var NAME = "&#x1F517; Occupancy" # display name of the plug-in
static var NAME = "Occupancy" # display name of the plug-in
static var ARG = "switch" # additional argument name (or empty if none)
static var ARG_TYPE = / x -> int(x) # function to convert argument to the right type
static var UPDATE_TIME = 5000 # update every 5s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Matter_Plugin_Bridge_Sensor end

class Matter_Plugin_Bridge_Sensor_Pressure : Matter_Plugin_Bridge_Sensor
static var TYPE = "http_pressure" # name of the plug-in in json
static var NAME = "&#x1F517; Pressure" # display name of the plug-in
static var NAME = "Pressure" # display name of the plug-in

static var CLUSTERS = {
0x0403: [0,1,2,0xFFFC,0xFFFD], # Pressure Measurement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Matter_Plugin_Bridge_Sensor end

class Matter_Plugin_Bridge_Sensor_Temp : Matter_Plugin_Bridge_Sensor
static var TYPE = "http_temperature" # name of the plug-in in json
static var NAME = "&#x1F517; Temperature" # display name of the plug-in
static var NAME = "Temperature" # display name of the plug-in

static var CLUSTERS = {
0x0402: [0,1,2,0xFFFC,0xFFFD], # Temperature Measurement p.97 - no writable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ class Matter_Plugin_Light0 : Matter_Plugin_Device

#############################################################
# Constructor
def init(device, endpoint, arguments)
super(self).init(device, endpoint, arguments)
def init(device, endpoint, config)
super(self).init(device, endpoint, config)
self.shadow_onoff = false
end

Expand Down
13 changes: 10 additions & 3 deletions lib/libesp32/berry_matter/src/embedded/Matter_Plugin_OnOff.be
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,17 @@ class Matter_Plugin_OnOff : Matter_Plugin_Device

#############################################################
# Constructor
def init(device, endpoint, arguments)
super(self).init(device, endpoint, arguments)
def init(device, endpoint, config)
super(self).init(device, endpoint, config)
self.shadow_onoff = false
self.tasmota_relay_index = int(arguments.find(self.ARG #-'relay'-#, 1))
end

#############################################################
# parse_configuration
#
# Parse configuration map
def parse_configuration(config)
self.tasmota_relay_index = int(config.find(self.ARG #-'relay'-#, 1))
if self.tasmota_relay_index <= 0 self.tasmota_relay_index = 1 end
end

Expand Down
Loading