WeatherFlow PiConsole - Customization

WeatherFlow PiConsole Customization Thread

The existing WeatherFlow PiConsole is so big, with over 2800 messages, that it exceeds the ability of the WF Forum software’s search capabilities to actually find something related to how to customize the software once it’s installed and running.

In the absence of objection or overruling by the mods :-), I’m creating this thread for discussions related solely to how to tweak the PiConsole to do more or different things. Hopefully this will make the ‘how do I extend it to do THIS’ kind of questions easier to figure out.

Example might indicate questions like:

  • how do I use ‘BigTemperature’ templates under ‘user’ to add a reading from an external source of data not currently known
  • how do I make that added template’s data refresh periodically
  • how do I alter the font etc. of the template

In short, any discussions about just about anything ‘other’ than how to install and configure the PiConsole to run at boot or related bug reports. That other thread is (HERE)

2 Likes

Question - how do I make the BigTemperature example .kv file actually display the value ?

The example BigTemperature .kv file has this in it:

<BigTemperaturePanel>:
    PanelBackground:
        _panelTitle: 'Big Temperature'
    Label:
        text: app.CurrentConditions.Obs['outTemp'][0] + app.CurrentConditions.Obs['outTemp'][1]

Cool. So when I hook it in by setting [PrimaryPanels] Panel2 = BigTemperature, my expectation is that I’d see the current temperature.

But the .py file has nothing in it at all. How does the PiConsole know what app.CurrentConditions.Obs is ? What needs to go in the .py file to make the variable called in the .kv file known ?

What makes the value refresh periodically ? How often would it refresh ? How would we know that ?

Now extend that use case a little. Consider trying to grab an external piece of information, say AQI from a REST interface or even from a local file stashed in /tmp on the filesystem. What would I put in the .py file to make a ‘new’ observation known and what would go in the .kv file to display it ?

Anybody have a ‘getting started extending PiConsole’ writeup that works with today’s version ?

1 Like

I really have to tip my hat to you for all the time you invest into this project.

I’m all about the world of open source software and the principal of sharing ideas for other to grow and expand upon, but it’s a rarity to see one single developer supporting a project to the level you do.

Frankly, the quickness of your responses and the courtesy and respect you provide to the users that need help goes far and beyond what I’ve seen many paid applications provide.

Anyways, I don’t have a question or anything of value to add other than, “Thanks for all your time and hard work”.

It sits in my office right next to my monitor and I watch it all day

4 Likes

That’s @peter for this great product. I"m just asking for help in how I can salt to taste a bit, so to speak, using the configurability hooks that are in there…

1 Like

This should work. Assuming you have renamed user\customPanels.kv.tmpl and user\customPanels.py.tmpl to user\customPanels.kv and user\customPanels.py, then setting [PrimaryPanels] Panel2 = BigTemperature should show the example BigTemperature panel in Panel 2. If it doesn’t then I need to look into this.

.kv files are intimately linked with the Kivy GUI library used to build the PiConsole. When a Kivy app is launched, it automatically loads a kv file that has the same name as the App class. The App class in main.py is defined as class wfpiconsole(App) (line 169), so the wfpiconsole.kv file is automatically loaded. Additional .kv files can be loaded using Builder.load_file('path/to/file.kv'), which is how the user\customPanels.kv file is loaded (line 195 of main.py). .kv files are used to design the layout of the different widgets (in the case of the PiConsole, widgets are essentially Panels), while the matching class in the .py file is used to control the behaviour of the widgets/panels.

In the .kv language, app always refers to the instance of your application class. CurrentConditions is an attribute of the wfpiconsole app class, that points to the instance of the CurrentConditions Screen class (line 422 of main.py). Obs is a Kivy property that is a member of the CurrentConditions class (line 416 of main.py).

It is the Kivy properties that handle the refreshing of the value. Whenever a new Websocket message arrives and is handled by the PiConsole, the value of the 'outTemp' field in the Obs DictProperty is updated, and this automatically triggers Kivy to update the value on the screen. It refreshes as often as a new Websocket message arrives.

This is where it gets a little more complex. First you would need to declare some Kivy properties to hold the information that you want to display from the external API in the .py file for your custom panel. panels/wind.py shows some good examples of this (rapidWindDir is a numeric property, windDirIcon is a string property). You will then see in kvland/wind.kv that these variables are referred to using root.rapidWindDir and root.windDirIcon. Next in your .py file you will want to write a function that calls the external API and updates the values of the properties you have previously defined. When these properties are updated, their value should automatically update on the screen. FInally in the init method in your .py file you will want to use Clock.schedule_interval to call the function that polls the external API at some set interval (see line 443 in main.py for an example).

Hopefully this all makes some sense!

This gives more info on the .kv lang: Kv language — Kivy 2.1.0 documentation

But I’m ultimately not going to use websockets at all. I’m going to grab data via other methods (UDP or MQTT or REST etc.). That PR that I submitted handles being able to spin up a minimal console without the websockets questions needing to be answered, which is the first step.

This is the second step - making data come into a panel from other than a websockets source.

That helps a lot. I couldn’t figure out how the variables were referred to.

Cool. Kivvy does the update magic.

Once I get it to display something once successfully :slight_smile:

Thanks a bunch. Time to see how far I get…

ok - I got a seemingly functional example to work so I thought I’d post it here. This very simple example sets a static value to display in a BigTemperature type panel.

Is this the right approach ?

(I didn’t worry if the button works so that’s untested completely)

#---------------------------------------
# user/customPanels.kv
#---------------------------------------
<ExamplePanel>:
    PanelBackground:
        _panelTitle: 'Example'
    Label:
        text: root.exampleData
        pos_hint: {'x': 0, 'y': 0}
        size_hint: (1, 1)
        font_name: 'fonts/Inter-Bold.ttf'
        font_size: dp(100*app.scaleFactor)
        color: utils.rgba('#ff0404ff')
        valign: 'center'
        halign: 'center'
        markup: 1
<ExampleButton>:
    PanelButton:
        text: 'Example'
        on_release: app.CurrentConditions.switchPanel(self)

and…

#-----------------------------
# user/customPanels.py
#------------------------------
from kivy.uix.relativelayout import RelativeLayout
from panels.template         import panelTemplate
class ExamplePanel(panelTemplate):

    def __init__(self,**kwargs):
        super().__init__(**kwargs)

    # this would be root.exampleData in the .kv file
    exampleData = "1.23"

Sorry - this was more confusing than I intended it to be. The refresh is not tied to a websocket message arriving. It’s simply that when a websocket message arrives, the Kivy properties that hold the different weather variables (e.g. outTemp, humidity etc.), are updated, and this automatically causes the console to refresh. You can use any data source to update a Kivy property, and this will trigger the console to refresh.

Yes, this is looking good so far in terms of code layout, but you need to make sure exampleData is a Kivy property, or else changing it’s value in the Python code will not trigger an update on the display. You want your Python code to look something like this

#-----------------------------
# user/customPanels.py
#------------------------------
from kivy.uix.relativelayout import RelativeLayout
from panels.template         import panelTemplate
from kivy.properties         import StringProperty

class ExamplePanel(panelTemplate):

    exampleData = StringProperty("0")

    def __init__(self,**kwargs):
        super().__init__(**kwargs)

        # this would be root.exampleData in the .kv file
        exampleData = "1.23"

There is a good example of designing widgets (i.e. panels) with .py and .kv files here: Designing with the Kivy Language

After reading the instructions for using the BigTemperature example, I decided to give it a try. Here are the steps I took to implement the custom panel.

Navigated to the user directory under the wfpiconsole installation (example: cd ~/wfpiconsole/user)
Copied the two template files to rename them to files that will be recognized by wfpiconsole:
cp --preserve customPanels.kv.tmpl customPanels.kv
cp --preserve customPanels.py.tmpl customPanels.py

Edited wfpiconsole.ini to change PanelTwo under [PrimaryPanels] to = BigTemperature.
Example:
[PrimaryPanels]
PanelOne = Forecast
PanelTwo = BigTemperature
PanelThree = WindSpeed
PanelFour = SunriseSunset
PanelFive = Rainfall
PanelSix = Lightning

Stopped and restarted wfpiconsole and I had a large temperature displayed in panel 2.

Crossposting a link to a nice example of extending PiConsole - Custom panel to display tide information - #7 by robert.jochim

ok - another dumb question. How would we get the [‘Station’][‘TempestID’] from the .ini file if we use a customPanel from the user directory ? I know ‘main.py’ already had the .ini file processed, but I’m lost re: the class hierarchy for how to get stuff main.py knows about into .py files down in the user customPanels.py

The information you are looking for is contained in the App class. This class can be accessed from any .py file (or classes in that file) by using:

from kivy.app import App
app = App.get_running_app()

The Tempest ID can then be accessed using

app.config['Station']['TempestID']

1 Like

Thanks.

Followup question - recall that I’m seeding the PiConsole with data grabbed by listening to the UDP messages rather than using websockets. Basically I’m running my wfudptools listener and saving to a scratch tmpfs file via cron, then using a modified PiConsole to read that data periodically and update the console display.

So where I’m at is trying to cram my data into your slots in the console panels whenever possible. Example draft brutal code is attached.

Rather then reinvent the wheel, I’d love to be able to call your ./lib routines to do all the post-processing stuff to set units, calculate derived values and labels, etc.

For example, if I had the current windspeed in a variable in user/CustomPanels.py how would I call your existing routine to calculate the word “Calm” to seed your wind panel with data ?

customPanels-draft.py.txt (5.7 KB)

Hi,

I have two panels that I like to change programmatically based on events. So for example when I am working on my laptop, and the WF PiConsole is right next to me, I like to see the detailed Temperature panel. But when my laptop is shut, it probably means I’m not near the PIConsole, so I like to see the BigTemp panel instead.

Currently, I achieve this by a script regularly pinging my laptop, if it doesn’t respond it edits the wfpiconsole.ini file and restarts the wfpiconsole service. This all works fine, but the restart takes 5 - 10 seconds. It would be neater if I could somehow tell wfpiconsole to re-read the ini file. Either by sending it a signal, or perhaps by running ‘systemctl reload wfpiconsole.service’ if it’s being run as a service?

I’m guessing I haven’t missed anything, and that this isn’t currently possible? Would this be useful a feature, and if so would it be a lot of work? I’ve had a quick poke around in the code, but not really found how or where to do this in the code.

The other use I have is that I do a similar thing for replacing the Solar panel with the Moon panel at civic sunset, and vice-versa at civic sunrise.

Any tips or suggestions most welcome, and thank @peter for the work creating this great console.

Sorry for the slow response - I have been out of town

All the functions required to derive further variables or calculate text descriptions are found in lib/derivedVariables.py. The can be imported into your custom panel using from lib import derivedVariables as derive and can then be called using derive.[function name].

For example, to get the word calm you would call derive.beaufortScale with the windSpeed as the input. The function returns a list containing the original wind speed, plus the matching Beafourt scale as a float, the same as a string, and the appropriate description. It is worth noting that the input windSpeed is also a list, with the windspeed in meters per second and a string containing the units, in this case mps. I would advise checking out lib/observationParser.py to see how I format the variables reported by devices and the variables that are derived.

1 Like

I don’t think this will be readily achievable without some significant modifications to the code. systemctl reload might work, but it probably will result in the same delay as there is a period of time required to restart the console. It certainly can’t be used to get Python to magically re-read the config file. That functionality would have to be added, along with a way of getting a script outside of Python to trigger a method inside a running instance of the console.

I think the better approach would be to do this all in pure Python. You could almost certainly recreate the script you are using to ping your laptop to run within the console, and then the console already has a method to switch between between primary and secondary panels. Check out the switchPanel method in the CurrentConditions class (line 475 of main.py) and line 611-613 of lib/observationParser.py to see how it is called

1 Like

Thanks - that helps a lot. I’ll get back to fiddling this week.

Thanks for the reply and the pointers. I’ll have a look at the switchPanel method code and have a fiddle!

1 Like

Hello,
I have been using the Weatherflow PiConsole for about a month now, and really like it.
I made a few changes to the code to display the time of max wind gust on the wind panel. If anyone else is interested in this I can submit it as a PR, or some other way. I made the changes to the main branch, but I’m sure I cound make the same changes to the develop branch if that would work better.

1 Like

Do you have a screenshot you could share?