uun_windsurfguru.modules package

Submodules

uun_windsurfguru.modules.WindSurfGuru module

class uun_windsurfguru.modules.WindSurfGuru.Overlay(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: IntEnum

Overlays of LED strip for different functionalities.

METER = 1

basic functionality

SPECIAL = 2

abnormal behaivour - SpecialState

class uun_windsurfguru.modules.WindSurfGuru.SpecialState(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: Enum

Special states of the LED strip signaling some abormality.

BLANK = 'blank'
ERROR = 'error'
NO_CONNECTION = 'noConnection'
class uun_windsurfguru.modules.WindSurfGuru.WindSurfGuru(config: dict, get_uucmd, device: LedDev = None)

Bases: Module

Module for visualization of wind speeds on a LED strip.

The method on_tick() is repeatedly executed by the :pkg:uun-iot library. The method gets data from uuCmd weatherConditions/getLast. If it successfuly retrieved the weather data, it will attempt to display them on the strip. If there was an error (internet connection error or any other code-related Exception), go into special state and display the error on the LED strip.


The display of error, or the actual wind speed data on the strip is exweather data, it will attempt to display them on the strip. If there was an error (internet connection error or any other code-related Exception), go into special state and display the error on the LED strip.

The LED strip device can be realized by various physical hardwares. Examples are:

  • a simple array of one-colored LEDs controled over GPIO,

  • the same array but with a I2C controller, or

  • an individually color-controllable LED strip such as Neopixel driven by an external library.

For each of these examples, there is a corresponding uun_iot_libledstrip.devices.LedDev driver – called GPIODev, I2CDev and NeopixelDev, respectively. All of these drivers inherit from the LedDev interface and are thus interchangeable. Of course, the simple one-colored LED strip cannot have different colours, but the driver behaves like they can – this is done for interchangeability of the underlying code. The code should not depend on the current realization of the hardware LED strip backend. The LED strip type can be chosen in configuration.


The display of an error, or the actual wind speed data on the strip, is exclusive – meaning that only the error, or the data can be displayed at the same time. For this reason, there is a uun_iot_libledstrip.StripOverlayBundle which provides exactly this exclusive access to the underlying hardware device.

On module initialization, two uun_iot_libledstrip.LedStrip-s are created. One responsible for the error display and the second uun_iot_libledstrip.MeterStrip, inheriting from class LedStrip, responsible for linear display of the wind speed on the strip. The StripOverlayBundle can then change in between the two by clearing the old one and activating the new one. This ensures exclusivity.

A LedStrip instance is created using a LedDev driver and a list or a dictionary of uun_iot_libledstrip.LedStripSegments-s. Each of the segments has a fixed LED position span (for example from 2nd to 5th LED) and the segments act as basic building blocks. Each of the segments can be lit with a solid color or can blink with given color and with given period. This display action can be encapsulated in a uun_iot_libledstrip.Action object, which is a “container” for these two kinds of visualization. Action can be stored in a segment and then activated later, activating the saved display action (blinking/solid color).


A uun_iot_libledstrip.MeterStrip bases on LedStrip and is composed of uun_iot_libledstrip.MeterStripSegment-s which base on LedStripSegment. Each MeterStripSegment is given a LedDev, array of led IDs and two crucial values: value_min and value_max. As the name MeterStripSegment suggests, the segment is supposed to measure some quantity. The segment linearly interpolates between tuning on no LEDs and turning on all of the allocated LEDs in the segment, based on value. The ratio of turned on LEDs is computed simply as value/(value_max-value_min) and the LEDs turn on up from lowest to highest IDs (or positions). This is done by virually decreasing the number of LEDs in a segment. If the value is less than value_min, all LEDs are off and if above value_max, all LEDs are on. The value can be set using a uun_iot_libledstrip.MeterStripSegment.set_value().


Additionaly, a MeterStripSegment can be initialized with an optional hook function. The hook function is called at the end of uun_iot_libledstrip.MeterStripSegment.set_value() and is given current value together with some MeterStripSegment properties. The function should return an Action, which will be stored into the segment and can be activated later. This can be used to dynamically change stored Action for the MeterStripSegment based on current value given in set_value().

The MeterStrip has a method with the same name set_value() which calls set_value on each of its segments and additionaly activates their action.

The following example illustrates this behaviour:

def hook(value, leds, action):
    if value > 100:
        action = Action(ActionType.SOLID, color=(255,0,0))
    else:
        action = Action(ActionType.SOLID, color=(0,255,0))
    return leds, action

segment = MeterStripSegment(
    device, autoshow=True, leds=[0,1,2,3,4],
    value_min=5, value_max=200,
    hook_set_value=hook
)

# definition of MeterStrip strip containing the segment
strip = MeterStrip(device,[segment])

strip.set_value(200)
strip.set_value(50)
>>> # the whole segment is lit red (255, 0, 0)
>>> # some LEDs in the segment are lit green (0, 255, 0)

This hook behaviour is used to set special actions if the wind speed is less than 15 knt or more than 30 knt for the first and the last segments. The whole process can be directly controlled from the configuration file.


In the configuration file, there are the definitions of the segments. Each segment contains a default action in keys color, action, interval. The LED positions are in the settings subkey together with definitions for minimal and maximal wind values for the given segment. The segment may have defined a specialAction key. The key contains information about the associated Action together with a condition key. The key indicates, when to activate the special action using the hook MeterStripSegment functionality.

Possible values of configuration <severity>/specialAction/condition are the following:

Each condition translates to one of:

  • ifWindBelow: wind < windMin,

  • ifWindBetween: windMin <= wind <= windMax,

  • ifWindGreater: windMax < wind,

where wind is the value of wind at the time of calling self._meter_strip.set_value(). If the wind conditions above are satisfied, apply the special action to the segment. Otherwise apply normal action.

Example of configuration file:

{
    "oidcGrantToken": {
        "gateway": "uuidentity.plus4u.net",
        "uuAppName": "uu-oidc-maing02",
        "awid": "xxx",
        "uuCmd": "oidc/grantToken",
        "tokenPath": "./oidc-token"
    },
    "uuThing": {
        "accessCode1": "xxx",
        "accessCode2": "xxx"
    },
    "uuApp": {
        "gateway": "uuapp.plus4u.net",
        "uuAppName": "ucl-weatherstation-maing01",
        "awid": "xxx",
        "uuCmdList": {
            "weatherConditionsGetLast": "weatherConditions/getLast"
        }
    },
    "gateway": {
        "moduleTimers": {
            "windSurfGuru": 120
        },
        "windSurfGuru": {
            "weatherStationCode": "MELTEMI",
            "ledStrip": {
                "device": "virtual",
                "pixels": 16,
                "pin": "D10"
            },
            "state": {
                "noConnection": {
                    "color": "#ff0000",
                    "action": "blink",
                    "interval": 0.2
                },
                "blank": {
                    "color": "#000000",
                    "action": "solid",
                    "interval": null
                },
                "error": {
                    "color": "#ff0000",
                    "action": "blink",
                    "interval": 0.2
                }
            },
            "severity": {
                "low": {
                    "color": "#0000ff",
                    "action": "solid",
                    "interval": null,
                    "specialAction": {
                        "condition": "ifWindBetween",
                        "color": "#0000ff",
                        "action": "blink",
                        "interval": 0.2
                    },
                    "settings": {
                        "windMin": 0,
                        "windMax": 14,
                        "ledMinPosition": 0,
                        "ledMaxPosition": 0
                    }
                },
                "light": {
                    "color": "#0000ff",
                    "action": "solid",
                    "interval": null,
                    "settings": {
                        "windMin": 16,
                        "windMax": 18,
                        "ledMinPosition": 1,
                        "ledMaxPosition": 3
                    }
                },
                "optimal": {
                    "color": "#00ff00",
                    "action": "solid",
                    "interval": null,
                    "settings": {
                        "windMin": 19,
                        "windMax": 22,
                        "ledMinPosition": 4,
                        "ledMaxPosition": 7
                    }
                },
                "heavy": {
                    "color": "#ff8000",
                    "action": "solid",
                    "interval": null,
                    "settings": {
                        "windMin": 23,
                        "windMax": 26,
                        "ledMinPosition": 8,
                        "ledMaxPosition": 11
                    }
                },
                "extreme": {
                    "color": "#ff0000",
                    "action": "solid",
                    "interval": null,
                    "specialAction": {
                        "condition": "ifWindGreater",
                        "color": "#ff0000",
                        "action": "blink",
                        "interval": 0.2
                    },
                    "settings": {
                        "windMin": 27,
                        "windMax": 30,
                        "ledMinPosition": 12,
                        "ledMaxPosition": 15
                    }
                }
            }
        }
    }
}
Parameters:
  • config – gateway configuration

  • get_uucmd – uucmd for getting new weather conditions, uucmd(weatherstation_code) -> requests.Response

  • device – optional external uun_iot_libledstrip.LedDev

Raises:

ValueError – if device was specified and is not an instance of LedDev

_abc_impl = <_abc._abc_data object>
_create_action_from_config(centry: dict) Action

Create Action object from configuration dictionary with keys {"action": ..., "color": ..., "interval": ...}.

Parameters:

centry – configuration dictionary. Key action can be solid or blink, color is in hex format and interval is in seconds.

Returns:

Action object constructed from centry

Return type:

Action

_device: LedDev
_init_hw() LedDev

Initialize underlying LED strip. Look at configuration ledStrip/device to determine what type of LED strip to use. Also read ledStrip/pixels to determine number of pixels to use. Some devices might use the ledStrip/pin setting (such as neopixel). The numbering of pin depends on used device.

ledStrip/device: neopixel, i2c, virtual (virtual is default, if none of the before were found)

_init_meter_segments() Dict[Any, MeterStripSegment]

Each severity key in configuration corresponds to one LED segment.

For severities which have special action specialAction and specialAction/condition configured, add hook_set_value hook to the severity segment. See uun_iot_libledstrip.MeterStripSegment for more information about hooks.

Possible values of configuration <severity>/specialAction/condition are the following. Each condition translates to one of:

  • ifWindBelow: wind < windMin,

  • ifWindBetween: windMin <= wind <= windMax,

  • ifWindGreater: windMax < wind,

where wind is the value of wind at the time of calling self._meter_strip.set_value(). If the wind conditions above are satisfied, apply the special action to the segment. Otherwise apply normal action.

_init_state_segments() Dict[Any, LedStripSegment]

Special states have a single common segment stretching over the whole strip with variable actions depending on state (see _set_special_state()).

_meter_segments: Dict[any, MeterStripSegment]
_sbundle: StripOverlayBundle

bundle a MeterStrip for normal usage and a general LedStrip for special state display

_set_special_state(state: SpecialState) None

Set application special state.

Display a SpecialState on the LED strip (currently whole strip, see _init_state_segments()), action for the strip/segments are taken from configuration.

Parameters:

stateSpecialState state to display

_wind_knt_min: float
evaluate_conditions(wind_speed_kph: float) None

Process wind speed and display it on a LED strip accordingly.

Parameters:

wind_speed_kph – wind speed in km/h

on_tick() None

Method to be called on each timer hit.

Receive information about weather from uuApp and call evaluate_conditions() to display the wind speed on the strip.

When ConnectionError occurs (when getting weather conditions), set special state to SpecialState.NO_CONNECTION using _set_special_state(). When any other exception occurs, set state to SpecialState.ERROR by same procedure.

Module contents

uun_windsurfguru.modules.init(config, uuclient: UuAppClient)