NEW Weewx extension to add Variable txt to a JPG

114 views
Skip to first unread message

Lorin Tremblay

unread,
Oct 15, 2023, 1:38:06 PM10/15/23
to weewx-user
I always wanted to learn how to code but never had time or a need. 

Now my first attempt to build an extension is in the works.

The 1st question is acceptable to ask help or opinions in this group/community? If so continue reading :)


Now I don’t know if this extension already exists, but I know that here a similar extension to do the same, but regardless I want it to be a learning experience.

I’m using pillow to add text to a jpg picture.

So far I capable of adding the txt to the picture with the variable that I want.
My problems occurs when I want added it in the unit_system (aka US, METRIC and METRICWX) and also rounding it to 2 decimal when there is more than 2.

Here is where I’m at right now 
Running on a Raspberry Pi3b with weewx 4.10.2 with all the current version as of this date 10/15/2023.


The weewx.conf is modified the following way:

[Engine]


    # The following section specifies which services should be run and in what order.

    [[Services]]

        prep_services = weewx.engine.StdTimeSynch

        data_services = user.airlink.AirLink, user.AddTxt2Jpg.AddTxt2Jpg

        process_services = weewx.engine.StdConvert, weewx.engine.StdCalibrate, weewx.engine.StdQC, weewx.wxservices.StdWXCalculate, user.aprx.WeewxAprx

        xtype_services = weewx.wxxtypes.StdWXXTypes, weewx.wxxtypes.StdPressureCooker, weewx.wxxtypes.StdRainRater, weewx.wxxtypes.StdDelta

        archive_services = weewx.engine.StdArchive

        restful_services = weewx.restx.StdStationRegistry, weewx.restx.StdWunderground, weewx.restx.StdPWSweather, weewx.restx.StdCWOP, weewx.restx.StdWOW, weewx.restx.StdAWEKAS, user.mqtt.MQTT

        report_services = weewx.engine.StdPrint, weewx.engine.StdReport


[AddTxt2Jpg]

    image_path = "/var/www/html/weewx/webcam/snap.jpg"

    output_path = "/var/www/html/weewx/webcam/snap_wx.jpg"

    font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"

    text_format = "Temperature: {outTemp}°C, Humidity: {outHumidity}%, Total rain:{rain24}, AQI: {pm2_5_aqi}"

    text_x = 50

    text_y = 1450

    text_color = white

    text_font_size = 36

    fields = "outTemp, outHumidity, pm2_5_aqi, windSpeed, barometer, rain24, windDir.ordinal_compass"


Now the script itself which can be or is a complete mess looks like this:

#!/usr/bin/python3

import weewx
from weewx.engine import StdService
import weeutil.weeutil
import syslog
from weewx.units import convert, getStandardUnitType

# Check for Pillow library installation
try:
    from PIL import Image, ImageDraw, ImageFont
except ImportError:
    syslog.syslog(syslog.LOG_ERR, "AddTxt2Jpg: Pillow library not found. Please install with 'pip install Pillow'.")
    exit(1)

class AddTxt2Jpg(StdService):
    def __init__(self, engine, config_dict):
        super(AddTxt2Jpg, self).__init__(engine, config_dict)
        
        # Logging tag
        self.log_tag = "AddTxt2Jpg"
        
        # Get configuration options from weewx.conf
        self.image_path = config_dict.get('AddTxt2Jpg', {}).get('image_path', '')
        self.output_path = config_dict.get('AddTxt2Jpg', {}).get('output_path', '')
        self.font_path = config_dict.get('AddTxt2Jpg', {}).get('font_path', '')
        self.text_format = config_dict.get('AddTxt2Jpg', {}).get('text_format', 'Weather Data: {outTemp}°C, Humidity: {outHumidity}%')
        self.text_x = int(config_dict.get('AddTxt2Jpg', {}).get('text_x', '20'))
        self.text_y = int(config_dict.get('AddTxt2Jpg', {}).get('text_y', '20'))
        self.text_color = config_dict.get('AddTxt2Jpg', {}).get('text_color', 'white')
        self.text_font_size = int(config_dict.get('AddTxt2Jpg', {}).get('text_font_size', '20'))
        
        # Set up the converter
        target_unit_nickname = config_dict.get("StdConvert", {}).get("target_unit", "METRIC")
        target_unit = weewx.units.unit_constants[target_unit_nickname.upper()]
        self.converter = weewx.units.StdUnitConverters[target_unit]

        # Get the list of fields to be extracted from the archive record
        fields = config_dict.get('AddTxt2Jpg', {}).get('fields', [])
        if isinstance(fields, str):
            self.fields = [x.strip() for x in fields.split(',')]
        elif isinstance(fields, list):
            self.fields = fields
        else:
            self.fields = []

        # Bind to the new archive record event
        self.bind(weewx.NEW_ARCHIVE_RECORD, self.new_archive_record)
        
        syslog.syslog(syslog.LOG_INFO, f"{self.log_tag}: initialized.")

    def new_archive_record(self, event):
        syslog.syslog(syslog.LOG_INFO, f"{self.log_tag}: New archive record event triggered.")
        
        try:
            # Access the record containing the archived weather data
            record = event.record

            # Convert all fields to the desired unit system using the converter
            for field in self.fields:
                if field in record:
                    value = record[field]
                    std_unit_type, group = getStandardUnitType(record['usUnits'], field)
                    value_converted = self.converter.convert((value, std_unit_type, group))
                    record[field] = value_converted[0]

            # Extract fields from configuration
            field_values = {field: record.get(field) for field in self.fields}

            # Open the existing image
            image = Image.open(self.image_path)

            # Create a drawing context
            draw = ImageDraw.Draw(image)

            # Load the specified font
            font = ImageFont.truetype(self.font_path, size=self.text_font_size)

            # Format the text using weather data from the record
            formatted_text = self.text_format.format(**field_values)

            # Add the formatted text to the image
            draw.text((self.text_x, self.text_y), formatted_text, fill=self.text_color, font=font)

            # Save the modified image to the output path
            image.save(self.output_path)

            # Log a message to the system log
            syslog.syslog(syslog.LOG_INFO, f"{self.log_tag}: Text added to image and saved as {self.output_path}")

        except Exception as e:
            # Handle any exceptions and log them
            syslog.syslog(syslog.LOG_ERR, f"{self.log_tag}: Error: {str(e)}")


How bad is it, I’m I far, can it be better coded (obviously YES) but is my first attempt any close.
PS I never coded before, this was done just by reading other extension making the best of it with chatgpt.

snap_wx-3.jpeg

Nate Bargmann

unread,
Jan 27, 2024, 8:51:04 PMJan 27
to weewx-user
I don't see that you ever got a reply.

Python f-strings allow for field width specifiers.  For example:

f"{record['outTemp']:.0f}°"
f"{record['barometer']:.2f}

Hope that helps.

- Nate
Reply all
Reply to author
Forward
0 new messages