2022

bluetooth Mesh models

a provisioner's perspective

EXTENDING MODELS

Light Lightness Setup Server

Light Lightness Server

Generic Power OnOff Setup Server

Generic Power OnOff Server

Generic Level Server

Generic OnOff Server

Generic Default Transition Time Server

EXTENDING MODELS

Light Lightness Setup Server

Light Lightness Server

Generic Power OnOff Setup Server

Generic Level Server

Generic Default Transition Time Server

Generic OnOff Server

Generic Power OnOff Server

models have separate states and opcodes

order does not matter

this isn't inheritance

STATE BINDING

STATE BINDING

Generic OnOff Server

  • Generic OnOff

Generic OnOff = F(Light Lightness Linear)

Generic Level = G(Light Lightness Linear)

Light Lightness Actual = H(Light Lightness Linear)

Light Lightness Linear = F'(Generic OnOff)

Light Lightness Linear = G'(Generic Level)

Light Lightness Linear = H'(Light Lightness Actual)

Light Lightness Server

Generic Level Server

Generic OnOff Server

  • Generic Level
  • Light Lightness Linear
  • Light Lightness Actual

SHARED SUBSCRIPTIONS

Light Lightness Server

Generic Power OnOff Server

Generic Level Server

Generic OnOff Server

Generic Default Transition Time Server

  • Subscription List
  • Subscription List
  • Subscription List
  • Subscription List
  • Subscription List

implementation detail:

keep lists in the element, identified  by root model id

extending models

Light Lightness Server

Generic Power OnOff Server

Generic Level Server

Generic OnOff Server

Generic Default Transition Time Server

Light CTL Server

Light CTL Temperature Server

Generic Level Server

Main

Temperature

Multiple elements

extending models

Multiple elements

https://www.bluetooth.org/errata/errata_view.cfm?errata_id=16366

https://www.bluetooth.org/errata/errata_view.cfm?errata_id=11345

EXTENDING MODELS

Multiple elements

Config Server

Health Server

Generic DTT Server

Scene Server

Scene Setup Server

Generic OnOff Server

Generic Power OnOff Server

Generic Power OnOff Setup Server

Generic Level Server

Light Lightness Server

Light Lightness Setup Server

Light CTL Server

Light CTL Setup Server

Sensor Server

Sensor Setup Server

Generic OnOff Client

Generic Level Client

Light LC Server

Light LC Setup Server

Generic OnOff Server

Sensor Server

Sensor Setup Server

Generic OnOff Client

Generic Level Client

Generic OnOff Server

Sensor Server

Sensor Setup Server

Light CTL Temperature Server

Generic DTT Server

Scene Server

Scene Setup Server

Generic Level Server

Generic OnOff Server

Scene Client

STATE BINDING

Multiple elements

STATE BINDING

Temperature.Generic Level = F(Temperature.Light CTL Temperature)

Temperature.Light CTL Temperature = G(Main.Light CTL)

Temperature.Light CTL Temperature = F'(Temperature.Generic Level)

Main.Light CTL = G'(Temperature.Light CTL Temperature)

Light CTL Server

Light CTL Temperature Server

Generic Level Server

  • Light CTL Temperature
  • Light CTL

Multiple elements

  • Generic Level

Light CTL Server

Main

Temperature

MULTIPLE MODELS

OF THE SAME TYPE

Config Server

Health Server

Generic DTT Server

Scene Server

Scene Setup Server

Generic OnOff Server

Generic Power OnOff Server

Generic Power OnOff Setup Server

Generic Level Server

Light Lightness Server

Light Lightness Setup Server

Light CTL Server

Light CTL Setup Server

Sensor Server

Sensor Setup Server

Generic OnOff Client

Generic Level Client

Light LC Server

Light LC Setup Server

Generic OnOff Server

Sensor Server

Sensor Setup Server

Generic OnOff Client

Generic Level Client

Generic OnOff Server

Sensor Server

Sensor Setup Server

Light CTL Temperature Server

Generic DTT Server

Scene Server

Scene Setup Server

Generic Level Server

Generic OnOff Server

Scene Client

#1

#2

DESIGNING AN API

Node
uuid: UUID
name: String
features: Features
elements: List<Element>
Element
index: Integer
models: Map<ModelId, Model>
Model
ID: ModelId
publication: Publication
app_keys: Set<Integer>

1

*

1

*

STATES

GenericOnOffServer
generic_on_off: Boolean
GenericLevelServer
generic_level: Integer
LightLightnessServer
light_lightness: Integer

DESIGNING AN API

Node
uuid: UUID
name: String
features: Features
elements: List<Element>
Element
index: Integer
subscription_lists: Map<ModelId, Set<int>>
models: Map<ModelId, Model>
Model
ID: ModelId
publication: Publication
app_keys: Set<Integer>
subscriptions: Set<int>

1

*

1

*

SUBSCRIPTION LISTS

GenericOnOffServer
generic_on_off: Boolean
GenericLevelServer
generic_level: Integer
LightLightnessServer
light_lightness: Integer

DESIGNING AN API

Node
uuid: UUID
name: String
features: Features
elements: List<Element>
Element
index: Integer
subscription_lists: Map<ModelId, Set<int>>
models: Map<ModelId, Model>
Model
ID: ModelId
publication: Publication
app_keys: Set<Integer>
subscriptions: Set<int>
ELEMENT: ModelElement
EXTEND: Map<ModelElement, Set<ModelId>>
corresponding(ModelId, ModelElement): Model

1

*

1

*

EXTENDING

ModelElement
MAIN
TEMPERATURE
HUE
SATURATION
LIGHT_LC
OLC
...

DESIGNING AN API

EXTENDING

class LightLCServer(Model):
    ID = ModelId(None, 0x130F)
    
    ELEMENT = ModelElement.LIGHT_LC
    EXTEND = {
        ModelElement.LIGHT_LC: (GenericOnOffServer,),
    }


class LightLightnessServer(Model):
    ID = ModelId(None, 0x1300)

    ELEMENT = ModelElement.MAIN
    EXTEND = {
        ModelElement.MAIN: (GenericPowerOnOffServer, GenericLevelServer),
        ModelElement.LIGHT_LC: (LightLCServer,),
    }
light_lightness is light_lc.corresponding(LightLightnessServer, ModelElement.MAIN)
light_lc is light_lightness.corresponding(LightLCServer, ModelElement.LIGHT_LC)

DESIGNING AN API

STATE BINDING

You Ain't Gonna Need It ?

DESIGNING AN API

SCENES

watch out for the next episode!

DESIGNING AN API

generating configuration

def diff(lhs_model, rhs_model):
    assert type(lhs_model) == type(rhs_model)
    
    # only if we own the subscription list
    if lhs_model.root:
        for missing_subscription in (
            lhs_model.subscriptions - rhs_model.subscriptions
        ):
            yield (StateAction.ADD, "subscriptions", missing_subscription)
    
        for extra_subscription in rhs_model.subscriptions - lhs_model.subscriptions:
            yield (StateAction.DEL, "subscriptions", missing_subscription)
     
    if lhs_model.publication != rhs_model.publication:
        yield (StateAction.SET, "publication", lhs_model.publication)

    for missing_bind in lhs_model.app_keys - rhs_model.app_keys:
        yield (StateAction.ADD, "app_keys", missing_bind)

    for extra_bind in rhs_model.app_keys - lhs_model.app_keys:
        yield (StateAction.DEL, "app_keys", extra_bind)

    for attribute in lhs_model.__attrs__:
        lhs_value = getattr(lhs_model, attribute.name)
        rhs_value = getattr(rhs_model, attribute.name)

        if lhs_value != rhs_value:
            yield (StateAction.SET, attribute.name, lhs_value)

DESIGNING AN API

generating configuration

def diff(lhs_element, rhs_element):
    for lhs_model, rhs_model in zip(
        sorted(lhs_element.models.values()), sorted(rhs_element.models.values())
    ):
        for (action, state, value) in lhs_model - rhs_model:
            yield Diff(lhs_element.index, type(lhs_model),
                       state, action, value)
            
def diff(lhs_node, rhs_node):
    for lhs_element, rhs_element in zip(lhs_node.elements, rhs_node.elements):
        yield from lhs_element.diff(rhs_element)
Diff(element=0, model=GenericLevelClient, state='app_keys',
     action=ADD', value=1)
Diff(element=0, model=GenericLevelClient, state='publication',
     action=SET, value=Publication(publish_address=49154, app_key_index=1, ...))
Diff(element=0, model=LightLightnessServer, state='app_keys',
     action=ADD, value=1)
Diff(element=0, model=LightLightnessSetupServer, state='subscriptions',
     action=ADD, value=49152)
Diff(element=0, model=LightLightnessSetupServer, state='subscriptions',
     action=ADD, value=49154)
Diff(element=1, model=GenericLevelClient, state='app_keys',
     action=ADD, value=1)
Diff(element=1, model=GenericLevelClient, state='publication',
     action='SET', value=Publication(publish_address=49156, app_key_index=1, ...)
Diff(element=1, model=LightCTLTemperatureServer, state='app_keys',
     action=ADD, value=1)
Diff(element=1, model=LightCTLTemperatureServer, state='subscriptions',
     action=ADD, value=49156)

2022

bluetooth Mesh models

a provisioner's perspective

Mesh models: provisioner perspective

By Michał Lowas-Rzechonek

Mesh models: provisioner perspective

  • 226