Determining Description and Icon from UDP api

I’ve built an integration which captures forecasts a few times a day by using the forecast rest endpoints. This all works well, but I’d like to have the current conditions be updated more frequently. I’ve got UDP listener capturing messages from the tempest, and can get all the UV/Rain/Wind etc.

My question is whether anyone has pseudo code on ways I can determine that I should show sun icon, and clear or cloudy etc.

Is it just that if precipitation type is none, base icon on UV and time of day?

Any pointers or pseudo code people have used to display that data from the data from UDP api, would be greatly greatly appreciated.

I don’t have any code and I don’t know for sure but my guess is that it simply uses the hourly forecast and the appropriate icon for that unless it detects rain/lightning, in which case it switches to a cloudy icon (with lightning when needed)

You should be able to pick out of this what you need. Not perfect, I’m in the process of moving Java-script running in Node-Red to Python. Below is what you need for cloudy and partly cloudy, if you want snow, fog or something else I can provide that too. You don’t need all that I provided but I just posted the function I am using and you can trim it down to your needs.

    def current_conditions(self, lightning_1h, precip_type, rain_rate, wind_speed, solar_el, solar_rad, solar_ins, snow_prob, fog_prob):
        """ Return local current conditions based on only weather station sesnors.
        Input:
            lightning_1h (#)
            **** Need to use the precip type number from Tempest so to get it before translations ****
            precip_type (#)
            rain_rate (imperial or metric)
            wind_speed (metric)
            solar_el (degrees)
            solar_rad (metric)
            snow_prob (%)
            fog_prob (%)
        Where:
            lightning_1h is lightning strike count within last hour
            precip_type is the type of precipitation: rain / hail
            rain_rate is the rain fall rate
            wind_speed is the speed of wind
            solar_el is the elevation of the sun with respect to horizon
            solar_rad is the measured solar radiation
            solar_ins is the calculated solar radiation
            snow_prob is the probability of snow
            fog_prob is the probability of fog
            si_p is the percentage difference in Solar Radiation and Solar Insolation
            si_d is the numeral difference in Solar Radiation and Solar Insolation
            cloudy is Boolan for cloud state
            part_cloud is Boolan for partly cloud state
            current is the Local Current Weather Condition
        """
        if (
            lightning_1h is None
            or precip_type is None
            or rain_rate is None
            or wind_speed is None
            or solar_el is None
            or solar_rad is None
            or solar_ins is None
            or snow_prob is None
            or fog_prob is None
        ):
            return None

        # Home Assistant weather conditions: clear-night, cloudy, fog, hail, lightning, lightning-rainy, partlycloudy, pouring, rainy, snowy, snowy-rainy, sunny, windy, windy-variant, exceptional
        # Exceptional not used here
        
        if (solar_el =< 0): # Can not determine clouds at night
            cloudy is False
            part_cloud is False
        else:
            si_p = round(((solar_rad) / (solar_ins)) * 100
            si_d = round((solar_ins) - (solar_rad))
            if ((si_p <= 50) and (si_d >= 50)):
                 cloudy is True
                 part_cloud is False
            elif ((si_p <= 75) and (abs(si_d) >= 15)):
                 part_cloud is True
                 cloudy is False
            elif ((si_p >= 115) and (abs(si_d) >= 15)):
                 part_cloud is True
                 cloudy is False
            else:
                 part_cloud is False
                 cloudy is False

        if ((lightning_1h >= 1) and (rain_rate >= 0.01)): # any rain at all
            current = "lightning-rainy"
        elif (lightning_1h >= 1):
            current = "lightning"
        elif (preip_type == 2):
            current = "hail"
        elif (rain_rate >= 7.8): # pouring => Imperial >= 0.31 in/hr, Metric >= 7.8 mm/hr
            current = "pouring"
        elif ((snow_prob >= 50) and (rain_rate >= 0.01)): # any rain at all
            current = "snowy-rainy"
        elif (rain_rate >= 0.01): # any rain at all
            current = "rainy"
        elif ((wind_speed >= 11.17) and (cloudy)): # windy => Imperial >= 25 mph, Metric >= 11.17 m/s
            current = "windy-variant"
        elif (wind_speed >= 11.17): # windy => Imperial >= 25 mph, Metric >= 11.17 m/s
            current = "windy"
        elif (fog_prob >= 50):
            current = "fog"
        elif ((snow_prob >= 50) and (cloudy)):
            current = "snowy"
        elif (cloudy == 'true'):
            current = "cloudy"
        elif (part_cloud):
            current = "partlycloudy"
        elif (solar_el => 0 ): # if daytime
            current = "sunny"
        else:
            current = "clear-night"

        # return the standard weather conditions as used by Home Assistant
        return current

'''    def current_conditions_txt(self, current_conditions):
        # Clear Night, Cloudy, Fog, Hail, Lightning, Lightning & Rain, Partly Cloudy, Pouring Rain, Rain, Snow, Snow & Rain, Sunny, Windy, Wind & Rain, exceptional (not used)
        # Need translations
        # Add input blurb

        if (current_conditions = "lightning-rainy"):
            current = "Lightning & Rain"
        elif (current_conditions = "lightning"):
            current = "Lightning"
        elif (current_conditions = "hail"):
            current = "Hail"
        elif (current_conditions = "pouring"):
            current = "Pouring Rain"
        elif (current_conditions = "snowy-rainy"):
            current = "Snow & Rain"
        elif (current_conditions = "rainy"):
            current = "Rain"
        elif (current_conditions = "windy-variant"):
            current = "Wind & Rain"
        elif (current_conditions = "windy"):
            current = "Windy"
        elif (current_conditions = "fog"):
            current = "Fog"
        elif (current_conditions = "snowy"):
            current = "Snow"
        elif (current_conditions = "cloudy"):
            current = "Cloudy"
        elif (current_conditions = "partlycloudy"):
            current = "Partly Cloudy"
        elif (current_conditions = "sunny"):
            current = "Sunny"
        elif (current_conditions = "clear-night"):
            current = "Clear Night"
        else
            current = "Unknown"

        # return the human readable weather conditions
        return current
'''

Solar Insolation is the estimated solar radiation at station without clouds:

    def solar_insolation(self, elevation, latitude, longitude):
        """ Return Estimation of Solar Radiation at current sun elevation angle.
        Input:
            Elevation in Meters
            Latitude
            Longitude
        Where:
            sz is Solar Zenith in Degrees
            ah is (Station Elevation Compensation) Constant ah_a = 0.14, ah_h = Station elevation in km
            am is Air Mass of atmoshere between Station and Sun
            1353 W/M^2 is considered Solar Radiation at edge of atmoshere
            ** All Trigonometry Fuctions need Degrees converted to Radians **
        """
        if elevation is None or latitude is None or longitude is None:
            return None

        # Calculate Solar Elevation
        solar_elevation = self.solar_elevation(latitude, longitude)

        cos = math.cos
        sin = math.sin
        asin = math.asin
        radians = math.radians
        degrees = math.degrees
        se = solar_elevation
        sz = 90 - se
        ah_a = 0.14
        ah_h = elevation / 1000
        ah = ah_a * ah_h
        if se >= 0:
            am = 1/(cos(radians(sz)) + 0.50572*pow((96.07995 - sz),(-1.6364)))
            si = (1353 * ((1-ah)*pow(.7, pow(am, 0.678))+ah))*(sin(radians(se)))
        else:
            am = 1
            si = 0
        si = round(si)

        return si

You will need solar elevation to calculate solar insolation:

    def solar_elevation(self, latitude, longitude):
        """ Return Sun Elevation in Degrees with respect to the Horizon.
        Input:
            Latitude in Degrees and fractional degrees (no docker variable yet)
            Longitude in Degrees and fractional degrees (no docker variable yet)
            Local Time
            UTC Time
        Where:
            jd is Julian Date (Day of Year only), then Julian Date + Fractional True
            lt is Local Time (####) 24 hour time, no colon
            tz is Time Zone Offset (ie -7 from UTC)
            beta is Beta for EOT
            lstm is Local Standard Time Meridian
            eot is Equation of Time
            tc is Time Correction Factor
            lst is Local Solar Time
            h is Hour Angle
            dec is Declination
            se is Solar Elevation
            ** All Trigonometry Fuctions need Degrees converted to Radians **
            ** The assumption is made that correct local time is established **
        """
        if latitude is None or longitude is None:
            return None

        cos = math.cos
        sin = math.sin
        asin = math.asin
        radians = math.radians
        degrees = math.degrees
        jd = time.localtime(time.time()).tm_yday
        hr = time.localtime(time.time()).tm_hour
        min = time.localtime(time.time()).tm_min
        lt = hr + min/60
        tz = time.localtime(time.time()).tm_gmtoff/3600
        jd = jd + lt/24
        beta = (360/365) * (jd - 81)
        lstm = 15 * tz
        eot = (9.87*(sin(radians(beta*2)))) - (7.53*(cos(radians(beta)))) - (1.5*(sin(radians(beta))))
        tc = (4 * (longitude - lstm)) + eot
        lst = lt + tc/60
        h = 15 * (lst - 12)
        dec = cos(radians(((jd) + 10) * (360/365))) * (-23.44)
        se = degrees(asin(sin(radians(latitude)) * sin(radians(dec)) + cos(radians(latitude)) * cos(radians(dec)) * cos(radians(h))))
        se = round(se)

        return se

Also, I have the Zambretti algorithm if you want forecasts to be local also. Zambretti may not be the best way to do it, but it works fairly well.

Sweet, I think I’ll do some experimentation converting this to languages I’m using, and see if I can get something even reasonably close.

I also wanted to say thanks… .I had no idea how complex this was going to be, and glad I have people that have already gone through similar things to be able to even tell me this algorithm might be the best.

That is just a tiny bit of what I’ve been assisting with using the UDP.

sensors:
  - absolute_humidity
  - air_density
  - air_temperature
  - battery # voltage
  - battery_mode # support for Tempest devices only
  - beaufort
  - cloud_base
  - delta_t
  - dewpoint
  - dewpoint_description
  - feelslike
  - fog_probability
  - freezing_level
  - illuminance
  - lightning_strike_count
  - lightning_strike_count_1hr
  - lightning_strike_count_3hr
  - lightning_strike_count_today
  - lightning_strike_distance
  - lightning_strike_energy
  - lightning_strike_time
  - precipitation_type
  - pressure_trend
  - rain_intensity
  - rain_rate
  - rain_start_time
  - rain_today
  - rain_yesterday
  - rain_duration_today
  - rain_duration_yesterday
  - relative_humidity
  - sealevel_pressure
  - snow_probability
  - solar_elevation
  - solar_insolation
  - solar_radiation
  - station_pressure
  - status
  - temperature_description
  - uv
  - uv_description
  - visibility
  - voltage
  - wbgt #Wet Bulb Globe Temperature
  - wetbulb
  - wind_bearing
  - wind_bearing_avg
  - wind_direction
  - wind_direction_avg
  - wind_gust
  - wind_lull
  - wind_speed
  - wind_speed_avg
  - weather
  - zambretti_number
  - zambretti_text

https://github.com/briis/hass-weatherflow2mqtt

I also have a lightning probability I’m working on,
Also, also I have an updated Snow Probability I’ve been testing.
Also, also, also testing apparent temperature which takes wind chill AND solar radiation to determine what temperature feels like to a person. “Feels Like Temperature” above is just Wind Chill effects.

The above repo might fit your needs and you can just add to it.

I feel like this repository might help me in more ways than one… but for the purpose of displaying current conditions, what I’m gonna have access to in mqtt is not going to include a description of current conditions or a suggestion I can use to decide which icon to display, correct? ( I dont know what an example of zambretti-number or zambretti_text are, and maybe that is what I need).

Zambretti Number is an arbitrary number assigned to the Forecast Condition. The original Zambretti used letter codes (1910ish, without looking it up). The text is the actual verbiage for the number. I choose to display the number so it could be graphed easily. The text is used in conjunction with translations for different languages.

    "zambretti": {
        "A": "Settled fine weather",
        "B": "Fine weather",
        "C": "Becoming fine",
        "D": "Fine, becoming less settled",
        "E": "Fine, possibly showers",
        "F": "Fairly fine, improving",
        "G": "Fairly fine, possibly showers early",
        "H": "Fairly fine, showers later",
        "I": "Showery early, improving",
        "J": "Changeable, improving",
        "K": "Fairly fine, showers likely",
        "L": "Rather unsettled, clearing later",
        "M": "Unsettled, probably improving",
        "N": "Showery, bright intervals",
        "O": "Showery, becoming unsettled",
        "P": "Changeable, some rain",
        "Q": "Unsettled, short fine intervals",
        "R": "Unsettled, rain later",
        "S": "Unsettled, rain at times",
        "T": "Very unsettled, finer at times",
        "U": "Rain at times, worse later",
        "V": "Rain at times, becoming very unsettled",
        "W": "Rain at frequent intervals",
        "X": "Very unsettled, rain",
        "Y": "Stormy, possibly improving",
        "Z": "Stormy, much rain"

I have several of these. . .like the bottom one. Still searching for the top one. . .which is exactly modeled after Negretti & Zambra London (circa 1915). They blended their names together to create Zambretti Forecaster.

3 Likes

that looks like fun. I could make one myself. How are the red letters on the bottom left one distributed? Equal distance? what is their order for the rising, steady and falling part?

@sunny

unfortunately I can’t pull the 3 discs apart (because the metal rivot which holds them all together). . .so here is a copy of the “Zambretti Weathercard Forecaster” (printed on heavy cardstock). You carefully cut out Figures 1 & 2. . .then place them on the card and secure with a metal brad.

Determine the wind direction (bottom portion of main card). . .and which season (Summer or Winter). . .and determine if Baro is Falling Steady or Rising. Then take the top disc (Figure 2) and use the arrow at the top to locate the approximate Baro in either inches or millibars.

The “coded” forecast will appear in the little window at the bottom.

Per example below. . .Baro is ~~ 1016.4mb and Rising with a NW wind. This give 10 which equates to Fine generally. The little plastic discs will be a little more accurate than a piece of paper.

Here is a website that produces similar results. (Says is for Lebanon. . .but actually works anywhere)

Zambretti Forecaster – Weatherwx.net

When you “Download files” from the bottom link. . .it will contain a Zip’d folder with 3 files that might be of interest / help to you.

The “Zambretti Forecaster” is sort of a “cousin” to the “Sager Weathercaster”. . .for which @peter has incorporated into the ‘wfpiconsole’.

2 Likes

very cool. this will work, and I could even figure out the the position of the original letters from this. Except that this one has 27 settings. But there are only 26 letters in the alphabet.

Here is an image of a guy who built this version.

and a snipit of code for Zambretti. . .

%{
Zambretti Forecaster by
Karl W. Berger
08 October 2018
based on work by Dr. Kevin F. Scott
DrKFS.net: A integer forecasting algorithm for barometers
curSLP is the current Sea Level Pressure in millibars or hPa
baroTrend is an index of the change in SLP over three hours:
mb Indication baroTrend

6.0 Rising Very Rapidly 4
3.6 Rising Quickly 3
1.6 Rising 2
0.1 Rising Slowly 1
-0.1 Steady 0
-1.6 Falling Slowly -1
-3.6 Falling -2
-6.0 Falling Quickly -3
<=-6.0 Falling Very Rapidly -4
}%

risingIndex = 1; % set to baroTrend index for
fallingIndex = -1; % determination of rising or falling
zambretti = “Forecast”; % forces ‘zambretti’ to be a string
if baroTrend <= fallingIndex %% FALLING %%
if curSLP > 1045
zambretti = “A: Settled Weather”;
elseif curSLP > 1032
zambretti = “B: Fine Weather”;
elseif curSLP > 1020
zambretti = “D: Fine, Becoming Less Settled”;
elseif curSLP > 1014
zambretti = “H: Fairly Fine, Showers Later”;
elseif curSLP > 1006
zambretti = “O: Showery, Becoming More Unsettled”;
elseif curSLP > 1000
zambretti = “R: Unsettled, Rain Later”;
elseif curSLP > 993
zambretti = “U: Rain At Time, Worse Later”;
elseif curSLP > 987
zambretti = “V: Rain At Times, Becomng Very Unsettled”;
else
zambretti = “X: Very Unsettled”;
end
elseif baroTrend >= risingIndex %% RISING %%
if curSLP > 1025
zambretti = “A: Settled Weather”;
elseif curSLP > 1016
zambretti = “B: Fine Weather”;
elseif curSLP > 1009
zambretti = “C: Becoming Fine”;
elseif curSLP > 1003
zambretti = “F: Fairly Fine, Improving”;
elseif curSLP > 997
zambretti = “G: Fairly Fine, Possible Showwers Early”;
elseif curSLP > 992
zambretti = “I: Showers Early, Improving”;
elseif curSLP > 986
zambretti = “J: Changeable, Mending”;
elseif curSLP > 980
zambretti = “L: Rather Unsettled, Clearing Later”;
elseif curSLP > 973
zambretti = “M: Unsettled, Probably Improving”;
elseif curSLP > 967
zambretti = “Q: Unsettled, Short Fine Intervals”;
elseif curSLP > 961
zambretti = “T: Very Unsettled, Finer At Times”;
elseif curSLP > 953
zambretti = “Y: Stormy, Possibly Improving”;
else
zambretti = “Z: Stormy, Much Rain”;
end
else %% STEADY %%
if curSLP > 1028
zambretti = “A: Settled Weather”;
elseif curSLP > 1017
zambretti = “B: Fine Weather”;
elseif curSLP > 1011
zambretti = “E: Fine, Possible Showers”;
elseif curSLP > 1003
zambretti = “K: Fairly Fine, Showers Likely”;
elseif curSLP > 996
zambretti = “N: Showery, Bright Intervals”;
elseif curSLP > 991
zambretti = “P: Changeable, Some Rain”;
elseif curSLP > 984
zambretti = “S: Unsettled, Rain At Times”;
elseif curSLP > 978
zambretti = “W: Rain At Frequent Intervals”;
elseif curSLP > 966
zambretti = “X: Very Unsettled, Rain”;
else
zambretti = “Z: Stormy, Much Rain”;
end
end
%% End of Zambretti snippet %%

So I got this all functional, and everything looks great… I can pull whatever data out of mqtt broker, even current state was returning clear-night or something. I was curious because zambretti at the same time was saying “Stormy, much rain” evne though it was cold but clear.

Zambretti is not current weather but typically you get the Zambretti reading at 0900 local time and it is the forecast for later (12 hours I think, I will be corrected I’m sure). It is not perfect but works well for something so simple.

that was a good link (DrKFS.net: A integer forecasting algorithm for barometers) thanks again!
The snippet of code is perhaps not complete or it is implementing something slightly different.
The pocket disc (and the link you send) also uses the wind direction and has winter/summer settings. So it has slightly more input. But the disk doesn’t has input for the speed of barometric pressure change.

Just for completeness:
use this with the language translation above

    def zambretti_value(self, latitude, wind_dir, p_hi, p_lo, trend, press):
        """ Return local forecast number based on Zambretti Forecaster.
        Input:
            Sea Level Pressure in mB
            Pressure Trend 0 for Steady, >0 for Rising and <0 for Falling
            Latitude in Degrees
            All Time Sea Level Pressure High in mB
            All Time Sea Level Pressure Low in mB
            Wind Direction in Degrees - Converted to Cardinal further down)
        Where:
        """
        if (
            latitude is None
            or wind_dir is None
            or p_hi is None
            or p_lo is None
            or trend is None
            or press is None
        ):
            return None

        # Based off Beteljuice's Zambretti work; http://www.beteljuice.co.uk/zambretti/forecast.html
        # Northern = 1 or Southern = 2 hemisphere
        if latitude >= 0:
            z_where = 1
        else:
            z_where = 2
        # upper limits of your local 'weather window' Pulled from All Time Max
        # var z_baro_top = 1050; # upper limits of your local 'weather window' (1050.0 hPa for UK)
        z_baro_top = p_hi
        # lower limits of your local 'weather window' Pulled from All Time Min
        # var z_baro_bottom = 950;	// lower limits of your local 'weather window' (950.0 hPa for UK)
        z_baro_bottom = p_lo
        # range of pressure
        z_range = z_baro_top - z_baro_bottom
        # z_hpa is Sea Level Adjusted (Relative) barometer in hPa or mB
        z_hpa = press
        # z_month is current month as a number between 1 to 12
        z_month = dt.datetime.now()
        z_month = int(z_month.strftime("%m"))
        # True (1) for summer, False (0) for Winter (Northern Hemishere)
        z_season = (z_month >= 4 and  z_month <= 9)
        # z_wind is English windrose cardinal eg. N, NNW, NW etc.
        # NB. if calm a 'nonsense' value should be sent as z_wind (direction) eg. 1 or calm !
        z_wind = self.direction(wind_dir)
        # z_trend is barometer trend: 0 = no change, 1 = rise, 2 = fall
        # z_trend_threshold = 0.047248 if self.unit_system == UNITS_IMPERIAL else 1.6
        if float(trend) < 0:
            z_trend = 2
        elif float(trend) > 0:
            z_trend = 1
        else:
            z_trend = 0
        # A constant for the current location, will vary since range will adjust as the min and max pressure will update overtime
        z_constant = (z_range / 22)
        # Equivalents of Zambretti 'dial window' letters A - Z: 0=A
        rise_options  = [25,25,25,24,24,19,16,12,11,9,8,6,5,2,1,1,0,0,0,0,0,0]
        steady_options  = [25,25,25,25,25,25,23,23,22,18,15,13,10,4,1,1,0,0,0,0,0,0]
        fall_options =  [25,25,25,25,25,25,25,25,23,23,21,20,17,14,7,3,1,1,1,0,0,0]

        if z_where == 1:
            # North hemisphere
            if z_wind == "N":
                z_hpa += 6 / 100 * z_range
            elif z_wind == "NNE":
                z_hpa += 5 / 100 * z_range
            elif z_wind == "NE":
                z_hpa += 5 / 100 * z_range
            elif z_wind == "ENE":
                z_hpa += 2 / 100 * z_range
            elif z_wind == "E":
                z_hpa -= 0.5 / 100 * z_range
            elif z_wind == "ESE":
                z_hpa -= 2 / 100 * z_range
            elif z_wind == "SE":
                z_hpa -= 5 / 100 * z_range
            elif z_wind == "SSE":
                z_hpa -= 8.5 / 100 * z_range
            elif z_wind == "S":
                z_hpa -= 12 / 100 * z_range
            elif z_wind == "SSW":
                z_hpa -= 10 / 100 * z_range
            elif z_wind == "SW":
                z_hpa -= 6 / 100 * z_range
            elif z_wind == "WSW":
                z_hpa -= 4.5 / 100 * z_range
            elif z_wind == "W":
                z_hpa -= 3 / 100 * z_range
            elif z_wind == "WNW":
                z_hpa -= 0.5 / 100 * z_range
            elif z_wind == "NW":
                z_hpa += 1.5 / 100 * z_range
            elif z_wind == "NNW":
                z_hpa += 3 / 100 * z_range
            if z_season == 1:
                # if Summer
                if z_trend == 1:
                    # rising
                    z_hpa += 7 / 100 * z_range
                elif z_trend == 2:
                    # falling
                    z_hpa -= 7 / 100 * z_range
        else:
            # South hemisphere
            if z_wind == "S":
                z_hpa += 6 / 100 * z_range
            elif z_wind == "SSW":
                z_hpa += 5 / 100 * z_range
            elif z_wind == "SW":
                z_hpa += 5 / 100 * z_range
            elif z_wind == "WSW":
                z_hpa += 2 / 100 * z_range
            elif z_wind == "W":
                z_hpa -= 0.5 / 100 * z_range
            elif z_wind == "WNW":
                z_hpa -= 2 / 100 * z_range
            elif z_wind == "NW":
                z_hpa -= 5 / 100 * z_range
            elif z_wind == "NNW":
                z_hpa -= 8.5 / 100 * z_range
            elif z_wind == "N":
                z_hpa -= 12 / 100 * z_range
            elif z_wind == "NNE":
                z_hpa -= 10 / 100 * z_range
            elif z_wind == "NE":
                z_hpa -= 6 / 100 * z_range
            elif z_wind == "ENE":
                z_hpa -= 4.5 / 100 * z_range
            elif z_wind == "E":
                z_hpa -= 3 / 100 * z_range
            elif z_wind == "ESE":
                z_hpa -= 0.5 / 100 * z_range
            elif z_wind == "SE":
                z_hpa += 1.5 / 100 * z_range
            elif z_wind == "SSE":
                z_hpa += 3 / 100 * z_range
            if z_season == 0:
                # Winter
                if z_trend == 1:
                    # rising
                    z_hpa += 7 / 100 * z_range
                elif z_trend == 2:
                    # falling
                    z_hpa -= 7 / 100 * z_range
            # END North / South

        if z_hpa == z_baro_top:
            z_hpa = z_baro_top - 1
        z_option = math.floor((z_hpa - z_baro_bottom) / z_constant)

        if z_option < 0:
            z_option = 0

        if z_option > 21:
            z_option = 21

        if z_trend == 1:
            # rising
            z_number = rise_options[z_option]
        elif z_trend == 2:
            # falling
            z_number = fall_options[z_option]
        else:
            # must be 'steady'
            z_number = steady_options[z_option]

        return z_number

    def zambretti_forecast(self, z_num: int):
        """ Return local forecast text based on Zambretti Number from the zambretti_num function.
        Input:
            Zambretti Number from Zambretti function
        Where:
        """
        if z_num is None:
            return None

        # Zambretti Text Equivalents of Zambretti 'dial window' letters A - Z
        # Simplified array for Language Translations
        z_forecast = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
        z_text = ""
        z_text += z_forecast[int(z_num)]

        return self.translations["zambretti"][z_text]

I realized that after I copied the code. . .the items on the left-hand margin were “cut-off” and so here is that URL that contains it in a better/readable format under the MATLAB code section in the middle. It also references Dr. Scott’s algorithm page again.

Zambretti Forecaster - IoT Kits (w4krl.com)

Let me know what you develop based in the info provided.

Apparently they still sell these under a different name as a desktop version https://mendipweather.co.uk/gifts/instruments/barometric-desktop-weather-forecaster/

@sunny

Oh silly me! Forgot to mention. . .I’ve got one of these too. . .sitting on the computer desk! Wind NE; Baro ~1020.5mb Rising; Today’s Forecast B: Fine Weather

image

I try to collect as many “weather toys” as I can.

Also have one of these. . .

I’m in the process of Replicating my own Storm Glass in a jar. I have all of the ingredients…except the NH4Cl (ammonium chloride)

  • Vodka, 100 Proof (50% Ethanol) - 300 mL
  • Camphor - 28g (1oz)
  • Potassium Nitrate - 10g
  • Ammonium Chloride - 10g