Merge branch 'master' into development

This commit is contained in:
Tom Keffer
2025-11-23 15:45:19 -08:00
20 changed files with 75 additions and 42 deletions

View File

@@ -3,8 +3,18 @@ WeeWX change history
### 5.2.1 MM/DD/YYYY
Remove unnecessary `UNIQUE` index on `PRIMARY KEY` columns in SQLite, achieving
size reduction of ~10%. Existing database schemas are not modified. Users
desiring size reduction are advised to consider manually migrating.
Fix problem that prevented `weectl database reconfigure` from working in cases
where a schema was specified.
Fix problem when importing data into a MySQL database. PR
#[1025](https://github.com/weewx/weewx/pull/1025). Thanks to user Robert!
[#1025](https://github.com/weewx/weewx/pull/1025). Thanks to user Robert!
Fix problem that prevented `weewxd` from restarting reliably if a MySQL
connection was lost. Fixes [Issue #1036](https://github.com/weewx/weewx/pull/1036).
### 5.2.0 10/05/2025
@@ -21,14 +31,13 @@ namespace. Addresses [Issue #993](https://github.com/weewx/weewx/issues/993).
The WeeWX Almanac is now extensible, allowing other astronomy packages to be
used besides `pyephem`. In particular, a WeeWX extension that uses the
[Skyfield](https://rhodesmill.org/skyfield/) package is available and can be
installed. See [Issue #981](https://github.com/weewx/weewx/issues/981) and [PR
#988](https://github.com/weewx/weewx/pull/988). Thanks to user Johanna!
installed. See [Issue #981](https://github.com/weewx/weewx/issues/981)
and [PR #988](https://github.com/weewx/weewx/pull/988). Thanks to user Johanna!
Fix LOGNAME bug in the weewx-multi sysV script. Thanks to Glenn McKechnie.
Added `illuminance` to the `wview_extended` schema. See [Issue
#992](https://github.com/weewx/weewx/issues/991) and [PR
#992](https://github.com/weewx/weewx/pull/992/files). Thanks to user Jeremy!
Added `illuminance` to the `wview_extended` schema. See [Issue #992](https://github.com/weewx/weewx/issues/991)
and [PR #992](https://github.com/weewx/weewx/pull/992/files). Thanks to user Jeremy!
Fix typo that causes phantom values of `soilMoist3` to appear with VantageVue
stations.
@@ -52,15 +61,13 @@ Ben Cotton!
In the Cumulus import code, the prefix `cur_` is used to signify a current value
for most observation types. However, there was one exception: `curr_in_temp`.
The utility and documentation have been changed to use `cur_in_temp` (one
'`r`'), making all types consistent. Fixes [Issue
#1006](https://github.com/weewx/weewx/issues/1006).
'`r`'), making all types consistent. Fixes [Issue #1006](https://github.com/weewx/weewx/issues/1006).
Fix a problem caused by an assumption that delta times are always in seconds.
Fixes issue [Issue #1009](https://github.com/weewx/weewx/issues/1009).
Fix bug that prevented arbitrary types from being used with `weectl database
add-column`. Fixes issue [Issue
#1007](https://github.com/weewx/weewx/issues/1007).
add-column`. Fixes issue [Issue #1007](https://github.com/weewx/weewx/issues/1007).
Fix bug that prevented MySQL and MariaDB versions greater than 10.0 from
working. Fixes issue [Issue #1010](https://github.com/weewx/weewx/issues/1010).

View File

@@ -124,6 +124,8 @@ directly underneath. When you're done, it should look something like this:
</table>
</div>
Finally, `weewx.restx.StdWOWBE` should be added into `restful_services`.
### Luminosity and Illuminance

View File

@@ -91,3 +91,15 @@ again assuming user `weewx` with password `weewx`. Adjust as necessary.
CREATE USER 'weewx'@'localhost' IDENTIFIED BY 'weewx';
GRANT select, update, create, delete, insert, alter, drop ON weewx.* TO weewx@localhost;
```
### 5. Make sure MySQL/MariaDB starts before WeeWX
Locate your WeeWX service file. Depending on your installation method, it will
located at either `/lib/systemd/system/weewx.service` or `/etc/systemd/system/weewx.service`).
Under the `[Unit]` section, add the following lines:
```ini
After=mariadb.service mysqld.service
Wants=mariadb.service mysqld.service
```
This will ensure that MySQL/MariaDB starts before WeeWX.

View File

@@ -1,5 +1,5 @@
#
# Copyright (c) 2009-2024 Tom Keffer <tkeffer@gmail.com>
# Copyright (c) 2009-2025 Tom Keffer <tkeffer@gmail.com>
#
# See the file LICENSE.txt for your full rights.
#
@@ -8,16 +8,19 @@
import decimal
try:
# Typically supplied by the "mysqlclient" package.
import MySQLdb
except ImportError:
# Maybe the user has "pymysql", a pure-Python version?
# Typically supplied by the "pymysql" package, a pure-Python version.
import pymysql as MySQLdb
from pymysql import DatabaseError as MySQLDatabaseError
from pymysql import (DatabaseError as MySQLDatabaseError,
InterfaceError as MySQLInterfaceError)
else:
try:
from MySQLdb import DatabaseError as MySQLDatabaseError
except ImportError:
from _mysql_exceptions import DatabaseError as MySQLDatabaseError
from _mysql_exceptions import (DatabaseError as MySQLDatabaseError,
InterfaceError as MySQLInterfaceError)
from weeutil.weeutil import to_bool, natural_compare
import weedb
@@ -60,6 +63,8 @@ def guard(fn):
# Default exception is weedb.DatabaseError
klass = exception_map.get(errno, weedb.DatabaseError)
raise klass(e)
except MySQLInterfaceError as e:
raise weedb.DisconnectError(e)
return guarded_fn

View File

@@ -268,7 +268,7 @@ class Cursor(sqlite3.Cursor, weedb.Cursor):
if obs_name in column_names:
continue
no_null_str = " NOT NULL" if no_null else ""
pk_str = " UNIQUE PRIMARY KEY" if pk else ""
pk_str = " PRIMARY KEY" if pk else ""
default_str = " DEFAULT %s" % default if default is not None else ""
create_list.append("`%s` %s%s%s%s" % (obs_name, obs_type, no_null_str,
pk_str, default_str))

View File

@@ -150,7 +150,7 @@ class TestMySQL(unittest.TestCase):
with Cursor(user='weewx1', password='weewx1') as cursor:
cursor.execute("CREATE DATABASE test_weewx1")
cursor.execute("CREATE TABLE test_weewx1.test1 "
"( dateTime INTEGER NOT NULL UNIQUE PRIMARY KEY, col1 int, col2 int)")
"( dateTime INTEGER NOT NULL PRIMARY KEY, col1 int, col2 int)")
cursor.execute("INSERT INTO test_weewx1.test1 "
"(dateTime, col1, col2) VALUES (1, 10, 20)")
with self.assertRaises(IntegrityError) as e:

View File

@@ -100,7 +100,7 @@ class TestSqlite3(unittest.TestCase):
def test_duplicate_key(self):
with Cursor(sqdb1) as cursor:
cursor.execute("CREATE TABLE test1 ( dateTime INTEGER NOT NULL UNIQUE PRIMARY KEY, col1 int, col2 int)")
cursor.execute("CREATE TABLE test1 ( dateTime INTEGER NOT NULL PRIMARY KEY, col1 int, col2 int)")
cursor.execute("INSERT INTO test1 (dateTime, col1, col2) VALUES (1, 10, 20)")
with self.assertRaises(IntegrityError) as e:
cursor.execute("INSERT INTO test1 (dateTime, col1, col2) VALUES (1, 30, 40)")

View File

@@ -180,7 +180,7 @@ class Common(unittest.TestCase):
weedb.create(db_dict)
connect = weedb.connect(db_dict)
cursor = connect.cursor()
cursor.execute("CREATE TABLE test1 ( dateTime INTEGER NOT NULL UNIQUE PRIMARY KEY, col1 int, col2 int)")
cursor.execute("CREATE TABLE test1 ( dateTime INTEGER NOT NULL PRIMARY KEY, col1 int, col2 int)")
cursor.execute("INSERT INTO test1 (dateTime, col1, col2) VALUES (1, 10, 20)")
with self.assertRaises(weedb.IntegrityError) as e:
cursor.execute("INSERT INTO test1 (dateTime, col1, col2) VALUES (1, 30, 40)")

View File

@@ -48,10 +48,10 @@ class Common(unittest.TestCase):
weedb.create(self.db_dict)
with weedb.connect(self.db_dict) as _connect:
with weedb.Transaction(_connect) as _cursor:
_cursor.execute("CREATE TABLE test1 (dateTime INTEGER NOT NULL UNIQUE PRIMARY KEY,"
_cursor.execute("CREATE TABLE test1 (dateTime INTEGER NOT NULL PRIMARY KEY,"
" min REAL, mintime INTEGER, max REAL, maxtime INTEGER, sum REAL,"
" count INTEGER, descript CHAR(20));")
_cursor.execute("CREATE TABLE test2 (dateTime INTEGER NOT NULL UNIQUE PRIMARY KEY,"
_cursor.execute("CREATE TABLE test2 (dateTime INTEGER NOT NULL PRIMARY KEY,"
" min REAL, mintime INTEGER, max REAL, maxtime INTEGER, sum REAL, "
"count INTEGER, descript CHAR(20));")
for irec in range(20):
@@ -151,7 +151,7 @@ class Common(unittest.TestCase):
with weedb.connect(self.db_dict) as _connect:
with _connect.cursor() as _cursor:
_cursor.execute(
"CREATE TABLE test1 (dateTime INTEGER NOT NULL UNIQUE PRIMARY KEY, x REAL )")
"CREATE TABLE test1 (dateTime INTEGER NOT NULL PRIMARY KEY, x REAL )")
# Now start the transaction
_connect.begin()
@@ -176,7 +176,7 @@ class Common(unittest.TestCase):
# create the table outside the transaction. We're not as concerned about a
# transaction failing when creating a table, because it only happens the first time
# weewx starts up.
_connect.execute("CREATE TABLE test1 (dateTime INTEGER NOT NULL UNIQUE PRIMARY KEY, "
_connect.execute("CREATE TABLE test1 (dateTime INTEGER NOT NULL PRIMARY KEY, "
"x REAL );")
# We're going to trigger the rollback by raising a bogus exception.

View File

@@ -65,9 +65,9 @@ import weedb
import weeutil.config
import weeutil.weeutil
import weewx.accum
import weewx.units
import weewx.xtypes
from weeutil.weeutil import timestamp_to_string, to_int, TimeSpan
from weewx.units import GenWithConvert
log = logging.getLogger(__name__)
@@ -708,8 +708,7 @@ def reconfig(old_db_dict, new_db_dict, new_unit_system=None, new_schema=None, dr
new_schema = weewx.schemas.wview_extended.schema
with Manager.open_with_create(new_db_dict, schema=new_schema) as new_archive:
# Wrap the input generator in a unit converter.
record_generator = weewx.units.GenWithConvert(old_archive.genBatchRecords(),
new_unit_system)
record_generator = GenWithConvert(old_archive.genBatchRecords(), new_unit_system)
if not dry_run:
# This is very fast because it is done in a single transaction context:
new_archive.addRecord(record_generator)
@@ -1017,7 +1016,7 @@ class DaySummaryManager(Manager):
# Schemas used by the daily summaries:
day_schemas = {
'scalar': [
('dateTime', 'INTEGER NOT NULL UNIQUE PRIMARY KEY'),
('dateTime', 'INTEGER NOT NULL PRIMARY KEY'),
('min', 'REAL'),
('mintime', 'INTEGER'),
('max', 'REAL'),
@@ -1028,7 +1027,7 @@ class DaySummaryManager(Manager):
('sumtime', 'INTEGER')
],
'vector': [
('dateTime', 'INTEGER NOT NULL UNIQUE PRIMARY KEY'),
('dateTime', 'INTEGER NOT NULL PRIMARY KEY'),
('min', 'REAL'),
('mintime', 'INTEGER'),
('max', 'REAL'),
@@ -1048,7 +1047,7 @@ class DaySummaryManager(Manager):
# SQL statements used by the metadata in the daily summaries.
meta_create_str = "CREATE TABLE %s_day__metadata (name CHAR(20) NOT NULL " \
"UNIQUE PRIMARY KEY, value TEXT);"
"PRIMARY KEY, value TEXT);"
meta_replace_str = "REPLACE INTO %s_day__metadata VALUES(?, ?)"
meta_select_str = "SELECT value FROM %s_day__metadata WHERE name=?"

View File

@@ -21,7 +21,7 @@
# with V4, a new style was added, which allows schema for the daily summaries
# to be expressed explicitly.
# =============================================================================
schema = [('dateTime', 'INTEGER NOT NULL UNIQUE PRIMARY KEY'),
schema = [('dateTime', 'INTEGER NOT NULL PRIMARY KEY'),
('usUnits', 'INTEGER NOT NULL'),
('interval', 'INTEGER NOT NULL'),
('barometer', 'REAL'),

View File

@@ -13,7 +13,7 @@
# =============================================================================
# NB: This schema is specified using the WeeWX V4 "new-style" schema.
# =============================================================================
table = [('dateTime', 'INTEGER NOT NULL UNIQUE PRIMARY KEY'),
table = [('dateTime', 'INTEGER NOT NULL PRIMARY KEY'),
('usUnits', 'INTEGER NOT NULL'),
('interval', 'INTEGER NOT NULL'),
('altimeter', 'REAL'),

View File

@@ -14,7 +14,7 @@
# =============================================================================
# NB: This schema is specified using the WeeWX V4 "new-style" schema.
# =============================================================================
table = [('dateTime', 'INTEGER NOT NULL UNIQUE PRIMARY KEY'),
table = [('dateTime', 'INTEGER NOT NULL PRIMARY KEY'),
('usUnits', 'INTEGER NOT NULL'),
('interval', 'INTEGER NOT NULL'),
('altimeter', 'REAL'),

View File

@@ -22,7 +22,7 @@ weeutil.logger.setup('weetest_database')
archive_sqlite = {'database_name': '/var/tmp/weewx_test/weedb.sdb', 'driver':'weedb.sqlite'}
archive_mysql = {'database_name': 'test_weedb', 'user':'weewx1', 'password':'weewx1', 'driver':'weedb.mysql'}
archive_schema = [('dateTime', 'INTEGER NOT NULL UNIQUE PRIMARY KEY'),
archive_schema = [('dateTime', 'INTEGER NOT NULL PRIMARY KEY'),
('usUnits', 'INTEGER NOT NULL'),
('interval', 'INTEGER NOT NULL'),
('barometer', 'REAL'),

View File

@@ -3,7 +3,7 @@
#
# See the file LICENSE.txt for your full rights.
#
table = [('dateTime', 'INTEGER NOT NULL UNIQUE PRIMARY KEY'),
table = [('dateTime', 'INTEGER NOT NULL PRIMARY KEY'),
('usUnits', 'INTEGER NOT NULL'),
('interval', 'INTEGER NOT NULL'),
('altimeter', 'REAL'),

View File

@@ -71,7 +71,7 @@ unit_system = metricwx
lightning_distance = Blitzentfernung
lightning_strike_count = Blitzanzahl
luminosity = Helligkeit
outHumidity = Luftfeuchte
outHumidity = Außenluftfeuchte
outTemp = Außentemperatur
pressure = abs. Luftdruck # QFE
pressureRate = Luftdruckänderung
@@ -96,7 +96,7 @@ unit_system = metricwx
windgustvec = Böen-Vektor
windrun = Windverlauf
windSpeed = Windgeschwindigkeit
windvec = Wind-Vektor
windvec = Windvektor
# used in Seasons skin but not defined
feel = gefühlte Temperatur
@@ -109,7 +109,7 @@ unit_system = metricwx
rainBatteryStatus = Regenmesser
referenceVoltage = Referenz
rxCheckPercent = Signalqualität
supplyVoltage = Versorgung
supplyVoltage = Versorgungsspannung
txBatteryStatus = Übertrager
windBatteryStatus = Anemometer
batteryStatus1 = Batterie1

View File

@@ -155,7 +155,7 @@ unit_system = metricwx
[Almanac]
# The labels to be used for the phases of the moon:
moon_phases = Nymåne, Voksende månesigd, Voksende halvmåne, Voksende fullmåne, Fullmåne, Minkende fullmåne, Minkende halvmåne, Minkende månesigd
moon_phases = Nymåne, Voksende sigd, Første kvarter, Voksende måne, Fullmåne, Avtakende måne, Siste kvarter, Avtakende sigd
[Texts]
"About this station" = "Om denne stasjonen"

View File

@@ -48,7 +48,7 @@
interval = Intervall
lightning_distance = Blitzentfernung
lightning_strike_count = Blitzanzahl
outHumidity = Luftfeuchte
outHumidity = Außenluftfeuchte
outTemp = Außentemperatur
pressure = abs. Luftdruck # QFE
pressureRate = Luftdruckänderung
@@ -65,7 +65,7 @@
windgustvec = Böen-Vektor
windrun = Windverlauf
windSpeed = Windgeschwindigkeit
windvec = Wind-Vektor
windvec = Windvektor
# used in Seasons skin but not defined
feel = gefühlte Temperatur
@@ -78,7 +78,7 @@
rainBatteryStatus = Regenmesser
referenceVoltage = Referenz
rxCheckPercent = Signalqualität
supplyVoltage = Versorgung
supplyVoltage = Versorgungsspannung
txBatteryStatus = Übertrager
windBatteryStatus = Anemometer

View File

@@ -44,7 +44,7 @@ unit_system = metricwx
[[Ordinates]]
# Ordinal directions. The last one should be for no wind direction
directions = N, NNE, NØ, ØNØ, Ø, ØSØ, SØ, SSØ, S, SSV, SV, VSV, V, VNV, NV, NNV, N/A
directions = N, NNØ, NØ, ØNØ, Ø, ØSØ, SØ, SSØ, S, SSV, SV, VSV, V, VNV, NV, NNV, N/A
[Labels]
@@ -111,7 +111,7 @@ unit_system = metricwx
[Almanac]
# The labels to be used for the phases of the moon:
moon_phases = Nymåne, Voksende månesigd, Halvmåne første kvarter, Voksende måne (ny), Fullmåne, Minkende måne (ne), Halvmåne siste kvarter, Minkende månesigd
moon_phases = Nymåne, Voksende sigd, Første kvarter, Voksende måne, Fullmåne, Avtakende måne, Siste kvarter, Avtakende sigd
[Texts]
"7-day" = "7-dager"

View File

@@ -8,6 +8,14 @@ After=time-sync.target
Wants=network-online.target
After=network-online.target
# If you are using MySQL uncomment the following lines:
# After=mysqld.service
# Wants=mysqld.service
# If you are using MariaDB uncomment the following lines:
# After=mariadb.service
# Wants=mariadb.service
[Service]
ExecStart=WEEWX_PYTHON WEEWXD WEEWX_CFGDIR/weewx.conf
StandardOutput=null