Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[weather.ozweather@matrix] 2.1.2 #2664

Merged
merged 1 commit into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions weather.ozweather/addon.xml
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="weather.ozweather" name="Oz Weather" version="2.1.1" provider-name="Bossanova808">
<addon id="weather.ozweather" name="Oz Weather" version="2.1.2" provider-name="Bossanova808">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
<import addon="script.module.requests" version="2.22.0+matrix.1"/>
<import addon="script.module.beautifulsoup4" version="4.8.2+matrix.1"/>
<import addon="script.module.pytz" version="3.0+matrix.2"/>
<import addon="script.module.bossanova808" version="1.0.0"/>
</requires>
<extension point="xbmc.python.weather" library="default.py"/>
<extension point="xbmc.addon.metadata">
<summary lang="en_GB">Weather forecasting and radar images for Australia using Bureau of Meteorology data</summary>
<description lang="en_GB">Weather forecasting and radar images for Australia using Bureau of Meteorology data. For full features (animated radars &amp; ABC weather videos) - make sure you install the replacement skin files - see information at the addon wiki (https://kodi.wiki/index.php?title=Add-on:Oz_Weather).</description>
<disclaimer lang="en_GB">Use of this addon implies you agree to the following terms and conditions - http://reg.bom.gov.au/other/copyright.shtml and http://reg.bom.gov.au/other/disclaimer.shtml</disclaimer>
<disclaimer lang="en_GB">Use of this addon implies you agree to the following terms and conditions - https://reg.bom.gov.au/other/copyright.shtml and https://reg.bom.gov.au/other/disclaimer.shtml</disclaimer>
<platform>all</platform>
<language>en</language>
<license>GPL-3.0-only</license>
Expand All @@ -22,8 +23,9 @@
<icon>icon.png</icon>
<fanart>fanart.jpg</fanart>
</assets>
<news>2.1.1
- Add Latitude and Longitude to Window data for skins that display 'season' using (arguably dubious) logic
<news>v2.1.2
- Remove old common code, instead use new script.module.bossanova808
- Fix for occasional error with missing national radar background
</news>
</extension>
</addon>
4 changes: 4 additions & 0 deletions weather.ozweather/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
v2.1.2
- Remove old common code, instead use new script.module.bossanova808
- Fix for occasional error with missing national radar background

2.1.1
- Add Latitude and Longitude to Window data for skins that display 'season' using (arguably dubious) logic

Expand Down
5 changes: 3 additions & 2 deletions weather.ozweather/default.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-

import sys
from bossanova808 import exception_logger
from resources.lib import ozweather

# This is kept as minimal as possible per @enen92's advice that:
# Reason for this is that entry point files are the only python files that are not compiled to bytecode (pyc).
# Hence, you'll see a performance gain if you store your codebase in a "module"

if __name__ == "__main__":
ozweather.run(sys.argv)

with exception_logger.log_exception():
ozweather.run(sys.argv)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 11 additions & 35 deletions weather.ozweather/resources/lib/abc/abc_video.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
# -*- coding: utf-8 -*-
import requests
import re
import sys
import xbmc
import json
from bs4 import BeautifulSoup

# Small hack to allow for unit testing - see common.py for explanation
# Allow for unit testing this file
# This brings this addon's resources, and bossanova808 module stuff into scope
# (when running this module outside Kodi)
if not xbmc.getUserAgent():
sys.path.insert(0, '../../..')
sys.path.insert(0, '../../../../script.module.bossanova808/resources/lib')

from resources.lib.store import Store
from resources.lib.common import *
from bossanova808.utilities import *


def scrape_and_play_abc_weather_video():
Expand All @@ -22,7 +23,7 @@ def scrape_and_play_abc_weather_video():
# Construct an offscreen list item with metadata...
item = xbmcgui.ListItem(path=url)
item.setProperty('mimetype', 'video/mpeg')
item.setInfo('Video', { 'title' : 'ABC Weather In 90 Seconds'})
item.setInfo('Video', {'title': 'ABC Weather In 90 Seconds'})
item.setArt({'thumb': f'{CWD}/resources/weather-in-90-seconds.png'})
# ...and then play it, fullscreen
xbmc.Player().play(url, item, False)
Expand All @@ -31,16 +32,12 @@ def scrape_and_play_abc_weather_video():

# See bottom of this file for notes on matching the video links (& Store.py for the regex)
def get_abc_weather_video_link():

try:
r = requests.get(Store.ABC_URL)

bs = BeautifulSoup(r.text, "html.parser")
json_string = bs.find("script", {'type': 'application/json',"id": "__NEXT_DATA__"})

json_string = bs.find("script", {'type': 'application/json', "id": "__NEXT_DATA__"})
json_object = json.loads(json_string.string)

# log(json_object)
# Logger.debug(json_object)
# Put the json blob into: https://jsonhero.io/j/JU0I9LB4AlLU
# Gives a path to the needed video as:
# $.props.pageProps.channelpage.components.0.component.props.list.3.player.config.sources.1.file
Expand All @@ -51,32 +48,11 @@ def get_abc_weather_video_link():
return sorted(urls, key=lambda x: x['bitrate'], reverse=True)[0]['file']

except Exception as inst:
log("Couldn't get ABC video URL from scraped page: " + str(inst))
Logger.error("Couldn't get ABC video URL from scraped page: " + str(inst))
return ""


# UNIT TESTING
if __name__ == "__main__":
log("\nTesting scraping of ABC Weather Video - here's the 'Best' link:\n")
log(get_abc_weather_video_link())


# > 2023_05 - CURRENT ABC VIDEO URL NOTES
# view the source on: https://www.abc.net.au/news/weather
# search for 'mp4'
# Regex in Store.py used to match the URL format
# Multiple matches will be found - first is a definition/download link (.mpg)
# 2nd is the highest quality stream (720p) - the one we want.
# https://mediacore-live-production.akamaized.net/video/01/im/Z/0m.mp4

# < 2023_05 - LEGACY INFO
# note date and quality level variables...
# view source on https://www.abc.net.au/news/newschannel/weather-in-90-seconds/ and find mp4 to see this list,
# the end of the URL can change regularly
# {'url': 'https://abcmedia.akamaized.net/news/news24/wins/201403/WINs_Weather1_0703_1000k.mp4', 'contentType': 'video/mp4', 'codec': 'AVC', 'bitrate': '928', 'width': '1024', 'height': '576', 'filesize': '11657344'}
# {'url': 'https://abcmedia.akamaized.net/news/news24/wins/201403/WINs_Weather1_0703_256k.mp4', 'contentType': 'video/mp4', 'codec': 'AVC', 'bitrate': '170', 'width': '320', 'height': '180', 'filesize': '2472086'}
# {'url': 'https://abcmedia.akamaized.net/news/news24/wins/201403/WINs_Weather1_0703_512k.mp4', 'contentType': 'video/mp4', 'codec': 'AVC', 'bitrate': '400', 'width': '512', 'height': '288', 'filesize': '5328218'}
# {'url': 'https://abcmedia.akamaized.net/news/news24/wins/201403/WINs_Weather1_0703_trw.mp4', 'contentType': 'video/mp4', 'codec': 'AVC', 'bitrate': '1780', 'width': '1280', 'height': '720', 'filesize': '21599356'}
# Other URLs - should match any of these
# https://abcmedia.akamaized.net/news/news24/wins/201409/WINm_Update1_0909_VSB03WF2_512k.mp4&
# https://abcmedia.akamaized.net/news/news24/wins/201409/WINs_Weather2_0209_trw.mp4
Logger.info("\nTesting scraping of ABC Weather Video - here's the 'Best' link:\n")
Logger.info(get_abc_weather_video_link())
58 changes: 30 additions & 28 deletions weather.ozweather/resources/lib/bom/bom_forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
import math
import xbmc

# Small hack to allow for unit testing - see common.py for explanation
# Allow for unit testing this file
# This brings this addon's resources, and bossanova808 module stuff into scope
# (when running this module outside Kodi)
if not xbmc.getUserAgent():
sys.path.insert(0, '../../..')
sys.path.insert(0, '../../../../script.module.bossanova808/resources/lib')

from resources.lib.store import Store
from resources.lib.common import *
from bossanova808.utilities import *
from bossanova808.logger import Logger

"""
(See bottom of this file for BOM API output examples!)
Expand Down Expand Up @@ -137,37 +141,35 @@ def bom_forecast(geohash):
try:
r = requests.get(bom_api_area_information_url)
area_information = r.json()["data"]
log(area_information)
Logger.debug(area_information)
if area_information:
location_timezone_text = area_information['timezone']
log(f"Location timezone from BOM is {location_timezone_text}")
Logger.debug(f"Location timezone from BOM is {location_timezone_text}")
location_timezone = pytz.timezone(location_timezone_text)
# For any date comparisons - this is the localised now...
now = datetime.datetime.now(location_timezone)

except:
log(f'Error retrieving area information from {bom_api_area_information_url}')
Logger.error(f'Error retrieving area information from {bom_api_area_information_url}')

# Get CURRENT OBSERVATIONS
try:
r = requests.get(bom_api_current_observations_url)
current_observations = r.json()["data"]
weather_data['ObservationsUpdated'] = utc_str_to_local_str(r.json()["metadata"]["issue_time"], time_zone=location_timezone)
weather_data['ObservationsStation'] = r.json()["data"]['station']['name']
log(current_observations)

Logger.debug(current_observations)
except:
log(f'Error retrieving current observations from {bom_api_current_observations_url}')
Logger.error(f'Error retrieving current observations from {bom_api_current_observations_url}')
return False

# Get WARNINGS
try:
r = requests.get(bom_api_warnings_url)
warnings = r.json()["data"]
log(warnings)
Logger.debug(warnings)

except:
log(f'Error retrieving warnings from {bom_api_warnings_url}')
Logger.error(f'Error retrieving warnings from {bom_api_warnings_url}')

# Get 7-DAY FORECAST
try:
Expand All @@ -176,10 +178,10 @@ def bom_forecast(geohash):
weather_data['ForecastUpdated'] = utc_str_to_local_str(r.json()["metadata"]["issue_time"], time_zone=location_timezone)
weather_data['ForecastRegion'] = r.json()["metadata"]["forecast_region"].title()
weather_data['ForecastType'] = r.json()["metadata"]["forecast_type"].title()
log(forecast_seven_days)
Logger.debug(forecast_seven_days)

except:
log(f'Error retrieving seven day forecast from {bom_api_forecast_seven_days_url}')
Logger.error(f'Error retrieving seven day forecast from {bom_api_forecast_seven_days_url}')
return False

# FUTURE?
Expand Down Expand Up @@ -234,15 +236,15 @@ def bom_forecast(geohash):
# AT = Ta + 0.33•ρ − 0.70•ws − 4.00
else:
try:
log("Feels Like not provided by BOM. Attempting to calculate feels like...but will likely fail...")
Logger.warning("Feels Like not provided by BOM. Attempting to calculate feels like...but will likely fail...")
water_vapour_pressure = current_observations['humidity'] * 6.105 * math.exp((17.27 * current_observations['temp'])/(237.7 + current_observations['temp']))
calculated_feels_like = current_observations['temp'] + (0.33 * water_vapour_pressure) - (0.70 * current_observations['wind']['speed_kilometre']) - 4.00
weather_data['Current.FeelsLike'] = calculated_feels_like
weather_data['Current.Ozw_FeelsLike'] = calculated_feels_like
log(f"Success! Using calculated feels like of {calculated_feels_like}")
Logger.info(f"Success! Using calculated feels like of {calculated_feels_like}")
# Not provided, could not calculate it, so set it to the current temp (avoids Kodi showing a random '0' value!)
except:
log("Feels like not provided, could not calculate - setting to current temperature to avoid kodi displaying random 0s.")
Logger.error("Feels like not provided, and also could not calculate - setting to current temperature to avoid kodi displaying random 0s.")
weather_data['Current.FeelsLike'] = str(round(current_observations['temp']))

# WARNINGS
Expand All @@ -251,7 +253,7 @@ def bom_forecast(geohash):
for i, warning in enumerate(warnings):
# Warnings body...only major warnings as we don't need every little message about sheep grazing etc...
if warning['warning_group_type'] == 'major':
# Don't really care when it was issue, if it hasn't expired, it's current, so show it..
# Don't really care when it was issue, if it hasn't expired, it's current, so show it...
# warning_issued = utc_str_to_local_str(warning['issue_time'], local_format='%d/%m %I:%M%p', time_zone=location_timezone)
# Time signature on the expiry is different for some reason?!
# Remove the completely unnecessary fractions of a second...
Expand Down Expand Up @@ -318,7 +320,7 @@ def bom_forecast(geohash):
descriptor_from_short_text = forecast_seven_days[i]['short_text'].lower()
descriptor_from_short_text = descriptor_from_short_text.replace(' ', '_').replace('.', '').strip()
if descriptor_from_short_text in Store.WEATHER_CODES:
log("Using short text as icon descriptor as this is often more reliable than the actual icon_descriptor")
Logger.debug("Using short text as icon descriptor as this is often more reliable than the actual icon_descriptor")
icon_descriptor = descriptor_from_short_text
# Now we have some sort of icon descriptor, try and get the actual icon_code
try:
Expand All @@ -327,11 +329,11 @@ def bom_forecast(geohash):
else:
icon_code = Store.WEATHER_CODES[icon_descriptor]
except KeyError:
log(f'Could not find icon code for BOM icon_descriptor: {forecast_seven_days[i]["icon_descriptor"]} and from short text {descriptor_from_short_text}')
Logger.error(f'Could not find icon code for BOM icon_descriptor: {forecast_seven_days[i]["icon_descriptor"]} and from short text {descriptor_from_short_text}')
# Pop the missing icon descriptor into the outlook to make it easier for people to report in the forum thread
set_key(weather_data, i, "Outlook", f"[{forecast_seven_days[i]['icon_descriptor']}] {forecast_seven_days[i]['short_text']}")

log(f"Icon descriptor is: {icon_descriptor}, icon code is {icon_code}")
Logger.debug(f"Icon descriptor is: {icon_descriptor}, icon code is {icon_code}")
set_keys(weather_data, i, ["OutlookIcon", "ConditionIcon"], f'{icon_code}.png')
set_keys(weather_data, i, ["FanartCode"], icon_code)

Expand All @@ -346,20 +348,20 @@ def bom_forecast(geohash):
temp_max = forecast_seven_days[i]['temp_max']
if i == 0 and not temp_max:
if forecast_seven_days[i]['now']['now_label'] == "Tomorrow's Max":
log("Using now->temp_now as now->now_label is Tomorrow's Max")
Logger.debug("Using now->temp_now as now->now_label is Tomorrow's Max")
temp_max = forecast_seven_days[i]['now']['temp_now']
elif forecast_seven_days[i]['now']['later_label'] == "Tomorrow's Max":
log("Using now->temp_later as now->later_label is Tomorrow's Max")
Logger.debug("Using now->temp_later as now->later_label is Tomorrow's Max")
temp_max = forecast_seven_days[i]['now']['temp_later']
set_keys(weather_data, i, ["HighTemp", "HighTemperature"], temp_max)

temp_min = forecast_seven_days[i]['temp_min']
if i == 0 and not temp_min:
if forecast_seven_days[i]['now']['now_label'] == 'Overnight Min':
log("Using now->temp_now as now->now_label is Overnight Min")
Logger.debug("Using now->temp_now as now->now_label is Overnight Min")
temp_min = forecast_seven_days[i]['now']['temp_now']
elif forecast_seven_days[i]['now']['later_label'] == 'Overnight Min':
log("Using now->temp_later as now->later_label is Overnight Min")
Logger.debug("Using now->temp_later as now->later_label is Overnight Min")
temp_min = forecast_seven_days[i]['now']['temp_later']
set_keys(weather_data, i, ["LowTemp", "LowTemperature"], temp_min)

Expand Down Expand Up @@ -411,19 +413,19 @@ def bom_forecast(geohash):


###########################################################
# MAIN (only for unit testing outside of Kodi)
# MAIN (only for unit testing outside Kodi)

if __name__ == "__main__":

geohashes_to_test = ['r1r11df', 'r1f94ew']
for geohash in geohashes_to_test:
log(f'Getting weather data from BOM for geohash "{geohash}"')
Logger.info(f'Getting weather data from BOM for geohash "{geohash}"')
test_data = bom_forecast(geohash)

for key in sorted(test_data):
if test_data[key] == "?" or test_data[key] == "na":
log("**** MISSING: ")
log(f'{key}: "{test_data[key]}"')
Logger.info("**** MISSING: ")
Logger.info(f'{key}: "{test_data[key]}"')

"""
BOM API
Expand Down
Loading
Loading