solarmoonpy 1.0.1__tar.gz → 1.0.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: solarmoonpy
3
- Version: 1.0.1
3
+ Version: 1.0.9
4
4
  Summary: Precise solar and lunar calculations for astronomical applications
5
5
  Author-email: figorr <jdcuartero@yahoo.es>
6
6
  License: Apache License
@@ -221,6 +221,9 @@ License-File: LICENSE
221
221
  Dynamic: license-file
222
222
 
223
223
  # ☀️🌙 solarmoonpy
224
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
225
+ ![Version](https://img.shields.io/pypi/v/solarmoonpy)
226
+
224
227
  Solar and moon calculations for Meteocat Home Assistant integration.
225
228
 
226
229
  **solarmoonpy** is a Python library that provides accurate calculations of solar and lunar positions, specifically designed to integrate with Home Assistant and Meteocat data.
@@ -229,7 +232,7 @@ This project enables you to obtain information such as sunrise, sunset, moon pha
229
232
 
230
233
  ## 🚀 Features
231
234
 
232
- - ☀️ Solar position calculations (sunrise, sunset, zenith, etc.).
235
+ - ☀️ Solar position calculations (sunrise, sunset, noon, etc.).
233
236
  - 🌙 Moon phase and position calculations.
234
237
  - Integration with meteorological data from Meteocat.
235
238
  - Compatible with Home Assistant for automations based on solar and lunar events.
@@ -1,4 +1,7 @@
1
1
  # ☀️🌙 solarmoonpy
2
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
3
+ ![Version](https://img.shields.io/pypi/v/solarmoonpy)
4
+
2
5
  Solar and moon calculations for Meteocat Home Assistant integration.
3
6
 
4
7
  **solarmoonpy** is a Python library that provides accurate calculations of solar and lunar positions, specifically designed to integrate with Home Assistant and Meteocat data.
@@ -7,7 +10,7 @@ This project enables you to obtain information such as sunrise, sunset, moon pha
7
10
 
8
11
  ## 🚀 Features
9
12
 
10
- - ☀️ Solar position calculations (sunrise, sunset, zenith, etc.).
13
+ - ☀️ Solar position calculations (sunrise, sunset, noon, etc.).
11
14
  - 🌙 Moon phase and position calculations.
12
15
  - Integration with meteorological data from Meteocat.
13
16
  - Compatible with Home Assistant for automations based on solar and lunar events.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "solarmoonpy"
7
- version = "1.0.1"
7
+ version = "1.0.9"
8
8
  description = "Precise solar and lunar calculations for astronomical applications"
9
9
  readme = "README.md"
10
10
  authors = [{ name = "figorr", email = "jdcuartero@yahoo.es" }]
@@ -1,9 +1,9 @@
1
1
  from dataclasses import dataclass
2
2
  from datetime import date, datetime, timezone
3
3
  from zoneinfo import ZoneInfo
4
- from typing import Optional, Union
4
+ from typing import Optional, Union, Literal, Dict
5
5
 
6
- from .sun import sunrise_sunset, noon # Importar funciones de sun.py
6
+ from .sun import sunrise_sunset, noon, dawn, dusk, midnight, sun_position # Importar funciones de sun.py
7
7
 
8
8
  @dataclass
9
9
  class LocationInfo:
@@ -253,20 +253,117 @@ class Location:
253
253
  date = datetime.now(self.tzinfo if local else timezone.utc).date()
254
254
 
255
255
  return noon(
256
- latitude=self.latitude,
257
256
  longitude=self.longitude,
258
257
  date=date,
259
258
  timezone=self.tzinfo if local else timezone.utc
260
259
  )
260
+
261
+ def dawn(
262
+ self,
263
+ date: Optional[date] = None,
264
+ local: bool = True,
265
+ twilight_type: Literal["civil", "nautical", "astronomical"] = "civil",
266
+ elevation: Optional[float] = None
267
+ ) -> Optional[datetime]:
268
+ """🌅 Calcula amanecer (inicio crepúsculo)."""
269
+ if local and self.timezone is None:
270
+ raise ValueError("Se solicitó hora local pero no se definió una zona horaria.")
261
271
 
262
- def sun_events(
272
+ date = date or datetime.now(self.tzinfo if local else timezone.utc).date()
273
+ elevation = elevation if elevation is not None else self.elevation
274
+
275
+ return dawn(
276
+ latitude=self.latitude,
277
+ longitude=self.longitude,
278
+ date=date,
279
+ twilight_type=twilight_type,
280
+ elevation=elevation,
281
+ timezone=self.tzinfo if local else timezone.utc,
282
+ with_refraction=True
283
+ )
284
+
285
+ def dusk(
263
286
  self,
264
287
  date: Optional[date] = None,
265
288
  local: bool = True,
289
+ twilight_type: Literal["civil", "nautical", "astronomical"] = "civil",
290
+ elevation: Optional[float] = None
291
+ ) -> Optional[datetime]:
292
+ """🌙 Calcula anochecer (fin crepúsculo)."""
293
+ if local and self.timezone is None:
294
+ raise ValueError("Se solicitó hora local pero no se definió una zona horaria.")
295
+
296
+ date = date or datetime.now(self.tzinfo if local else timezone.utc).date()
297
+ elevation = elevation if elevation is not None else self.elevation
298
+
299
+ return dusk(
300
+ latitude=self.latitude,
301
+ longitude=self.longitude,
302
+ date=date,
303
+ twilight_type=twilight_type,
304
+ elevation=elevation,
305
+ timezone=self.tzinfo if local else timezone.utc,
306
+ with_refraction=True
307
+ )
308
+
309
+ def midnight(
310
+ self,
311
+ date: Optional[date] = None,
312
+ local: bool = True
313
+ ) -> datetime:
314
+ """🌑 Calcula medianoche solar."""
315
+ if local and self.timezone is None:
316
+ raise ValueError("Se solicitó hora local pero no se definió una zona horaria.")
317
+
318
+ date = date or datetime.now(self.tzinfo if local else timezone.utc).date()
319
+
320
+ return midnight(
321
+ longitude=self.longitude,
322
+ date=date,
323
+ timezone=self.tzinfo if local else timezone.utc
324
+ )
325
+
326
+ def sun_position(
327
+ self,
328
+ dt: Optional[datetime] = None,
329
+ local: bool = True,
266
330
  elevation: Optional[float] = None
267
331
  ) -> dict:
268
332
  """
269
- Devuelve un diccionario con sunrise, noon y sunset para una fecha dada.
333
+ Calcula la posición actual del Sol.
334
+
335
+ Args:
336
+ dt: Momento exacto. Si None, usa ahora.
337
+ local: True para usar zona horaria local.
338
+ elevation: Elevación del observador.
339
+
340
+ Returns:
341
+ dict: elevation, azimuth, above_horizon, rising
342
+ """
343
+ if local and self.timezone is None:
344
+ raise ValueError("Se solicitó hora local pero no se definió una zona horaria.")
345
+
346
+ tz = self.tzinfo if local else timezone.utc
347
+ dt = dt or datetime.now(tz)
348
+ elevation = elevation if elevation is not None else self.elevation
349
+
350
+ return sun_position(
351
+ latitude=self.latitude,
352
+ longitude=self.longitude,
353
+ dt=dt,
354
+ elevation=elevation,
355
+ timezone=tz,
356
+ with_refraction=True
357
+ )
358
+
359
+ def sun_events(
360
+ self,
361
+ date: Optional[date] = None,
362
+ local: bool = True,
363
+ elevation: Optional[float] = None,
364
+ ) -> Dict:
365
+ """
366
+ Devuelve un diccionario con los eventos solares para una fecha dada, incluyendo todos los tipos de crepúsculo.
270
367
 
271
368
  Args:
272
369
  date: Fecha para la cual calcular los eventos solares. Si es None, usa la fecha actual.
@@ -275,16 +372,25 @@ class Location:
275
372
 
276
373
  Returns:
277
374
  dict: {
375
+ "dawn_civil": datetime | None,
376
+ "dawn_nautical": datetime | None,
377
+ "dawn_astronomical": datetime | None,
278
378
  "sunrise": datetime,
279
379
  "noon": datetime,
280
- "sunset": datetime
380
+ "sunset": datetime,
381
+ "dusk_civil": datetime | None,
382
+ "dusk_nautical": datetime | None,
383
+ "dusk_astronomical": datetime | None,
384
+ "midnight": datetime,
385
+ "daylight_duration": float | None
281
386
  }
282
387
  """
283
388
  if local and self.timezone is None:
284
389
  raise ValueError("Se solicitó hora local pero no se definió una zona horaria.")
285
390
 
286
391
  tz = self.tzinfo if local else timezone.utc
287
- date = date or datetime.now(tz).date()
392
+ now = datetime.now(tz)
393
+ date = date or now.date()
288
394
  elevation = elevation if elevation is not None else self.elevation
289
395
 
290
396
  # Calcular amanecer y atardecer
@@ -299,14 +405,96 @@ class Location:
299
405
 
300
406
  # Calcular mediodía solar
301
407
  noon_time = noon(
408
+ longitude=self.longitude,
409
+ date=date,
410
+ timezone=tz,
411
+ )
412
+
413
+ # Calcular medianoche solar
414
+ midnight_time = midnight(
415
+ longitude=self.longitude,
416
+ date=date,
417
+ timezone=tz,
418
+ )
419
+
420
+ # Calcular duración del día (en horas)
421
+ daylight_duration = None
422
+ if sunrise and sunset:
423
+ daylight_duration = (sunset - sunrise).total_seconds() / 3600
424
+
425
+ # Calcular dawn y dusk para cada tipo de crepúsculo
426
+ dawn_civil = dawn(
427
+ latitude=self.latitude,
428
+ longitude=self.longitude,
429
+ date=date,
430
+ twilight_type="civil",
431
+ elevation=elevation,
432
+ timezone=tz,
433
+ with_refraction=True,
434
+ )
435
+ dusk_civil = dusk(
302
436
  latitude=self.latitude,
303
437
  longitude=self.longitude,
304
438
  date=date,
439
+ twilight_type="civil",
440
+ elevation=elevation,
305
441
  timezone=tz,
442
+ with_refraction=True,
306
443
  )
444
+ dawn_nautical = dawn(
445
+ latitude=self.latitude,
446
+ longitude=self.longitude,
447
+ date=date,
448
+ twilight_type="nautical",
449
+ elevation=elevation,
450
+ timezone=tz,
451
+ with_refraction=True,
452
+ )
453
+ dusk_nautical = dusk(
454
+ latitude=self.latitude,
455
+ longitude=self.longitude,
456
+ date=date,
457
+ twilight_type="nautical",
458
+ elevation=elevation,
459
+ timezone=tz,
460
+ with_refraction=True,
461
+ )
462
+ dawn_astronomical = dawn(
463
+ latitude=self.latitude,
464
+ longitude=self.longitude,
465
+ date=date,
466
+ twilight_type="astronomical",
467
+ elevation=elevation,
468
+ timezone=tz,
469
+ with_refraction=True,
470
+ )
471
+ dusk_astronomical = dusk(
472
+ latitude=self.latitude,
473
+ longitude=self.longitude,
474
+ date=date,
475
+ twilight_type="astronomical",
476
+ elevation=elevation,
477
+ timezone=tz,
478
+ with_refraction=True,
479
+ )
480
+
481
+ # Posición actual del Sol
482
+ pos = self.sun_position(dt=now, local=local, elevation=elevation)
307
483
 
308
484
  return {
485
+ "dawn_civil": dawn_civil,
486
+ "dawn_nautical": dawn_nautical,
487
+ "dawn_astronomical": dawn_astronomical,
309
488
  "sunrise": sunrise,
310
489
  "noon": noon_time,
311
490
  "sunset": sunset,
312
- }
491
+ "dusk_civil": dusk_civil,
492
+ "dusk_nautical": dusk_nautical,
493
+ "dusk_astronomical": dusk_astronomical,
494
+ "midnight": midnight_time,
495
+ "daylight_duration": daylight_duration,
496
+ "sun_elevation": pos["elevation"],
497
+ "sun_azimuth": pos["azimuth"],
498
+ "sun_horizon_position": pos["horizon_position"],
499
+ "sun_rising": pos["rising"],
500
+ }
@@ -374,4 +374,50 @@ def moon_elongation(date_utc: date) -> float:
374
374
  jc = julianday_to_juliancentury(jd)
375
375
  lambda_m, _, _, _ = _moon_ecliptic_position(jd)
376
376
  lambda_s = sun_apparent_long(jc)
377
- return _normalize_angle(lambda_m - lambda_s)
377
+ return _normalize_angle(lambda_m - lambda_s)
378
+
379
+ def get_moon_phase_name(d: date) -> str:
380
+ """Determina el nombre de la fase lunar para la fecha dada."""
381
+ percentage = illuminated_percentage(d)
382
+ elongation = moon_elongation(d)
383
+
384
+ # Determinar nombre intermedio basado en elongación y porcentaje iluminado
385
+ is_waxing = elongation < 180.0 # <180° creciente, >=180° menguante
386
+ if percentage < 50.0:
387
+ phase_name = "waxing_crescent" if is_waxing else "waning_crescent"
388
+ else:
389
+ phase_name = "waxing_gibbous" if is_waxing else "waning_gibbous"
390
+
391
+ # Verificar fases primarias exactas y override si corresponde
392
+ last_new = find_last_phase_exact(d, 0.0)
393
+ if last_new and last_new.date() == d:
394
+ return "new_moon"
395
+
396
+ last_first = find_last_phase_exact(d, 90.0)
397
+ if last_first and last_first.date() == d:
398
+ return "first_quarter"
399
+
400
+ last_full = find_last_phase_exact(d, 180.0)
401
+ if last_full and last_full.date() == d:
402
+ return "full_moon"
403
+
404
+ last_last = find_last_phase_exact(d, 270.0)
405
+ if last_last and last_last.date() == d:
406
+ return "last_quarter"
407
+
408
+ return phase_name
409
+
410
+ def get_lunation_duration(date_utc: date) -> str:
411
+ """Calcula la duración exacta de la lunación actual (desde la última luna nueva hasta la siguiente)."""
412
+ last_new = find_last_phase_exact(date_utc, 0.0)
413
+ # Buscar la siguiente luna nueva: empezar ~25 días después (buffer seguro)
414
+ search_date = (last_new + timedelta(days=25)).date()
415
+ next_new = find_last_phase_exact(search_date + timedelta(days=10), 0.0) # Buffer para asegurar el cruce
416
+ if next_new <= last_new:
417
+ next_new = find_last_phase_exact(search_date + timedelta(days=35), 0.0) # Seguridad extra si falla
418
+ duration = next_new - last_new
419
+ days = duration.days
420
+ secs = duration.seconds
421
+ hours = secs // 3600
422
+ minutes = (secs % 3600) // 60
423
+ return f"{days}d {hours}h {minutes}m"
@@ -1,5 +1,15 @@
1
1
  import datetime
2
2
  from math import acos, asin, atan2, cos, degrees, radians, sin, tan, sqrt
3
+ from typing import Optional, Tuple, Literal
4
+
5
+ # Constantes estándar para diferentes eventos solares
6
+ SUN_APPARENT_RADIUS = 959.63 / 3600.0 # ≈ 0.2666° (radio aparente del Sol)
7
+
8
+ # Zenith angles estándar (90° = horizonte)
9
+ ZENITH_SUNRISE_SUNSET = 90.0 + SUN_APPARENT_RADIUS
10
+ ZENITH_CIVIL_DAWN_DUSK = 96.0
11
+ ZENITH_NAUTICAL_DAWN_DUSK = 102.0
12
+ ZENITH_ASTRONOMICAL_DAWN_DUSK = 108.0
3
13
 
4
14
  def julianday(date_or_dt: datetime.date | datetime.datetime) -> float:
5
15
  """
@@ -239,4 +249,185 @@ def noon(
239
249
  time_utc = (720.0 - (4 * longitude) - eqtime) # minutos
240
250
  noon_utc = datetime.datetime(date.year, date.month, date.day, tzinfo=datetime.timezone.utc) \
241
251
  + datetime.timedelta(minutes=time_utc)
242
- return noon_utc.astimezone(timezone)
252
+ return noon_utc.astimezone(timezone)
253
+
254
+ def dawn(
255
+ latitude: float, longitude: float, date: datetime.date,
256
+ twilight_type: Literal["civil", "nautical", "astronomical"] = "civil",
257
+ elevation: float = 0.0,
258
+ timezone: datetime.timezone = datetime.timezone.utc,
259
+ with_refraction: bool = True
260
+ ) -> Optional[datetime.datetime]:
261
+ """🌅 Calcula amanecer (inicio crepúsculo)."""
262
+ zeniths = {"civil": 96.0, "nautical": 102.0, "astronomical": 108.0}
263
+ zenith = zeniths[twilight_type]
264
+
265
+ # Reutiliza la misma lógica que sunrise_sunset pero para un zenith específico
266
+ adjustment_for_elevation = adjust_to_horizon(elevation)
267
+ adjusted_zenith = zenith + adjustment_for_elevation
268
+ if with_refraction:
269
+ adjusted_zenith += refraction_at_zenith(adjusted_zenith)
270
+
271
+ latitude = max(min(latitude, 89.8), -89.8)
272
+ jd = julianday(date)
273
+ jc = julianday_to_juliancentury(jd)
274
+ declination = sun_declination(jc)
275
+ eqtime = eq_of_time(jc)
276
+
277
+ try:
278
+ ha_rising = hour_angle(latitude, declination, adjusted_zenith, "rising")
279
+ except ValueError:
280
+ return None
281
+
282
+ delta = -longitude - degrees(ha_rising)
283
+ time_utc_minutes = 720.0 + (delta * 4.0) - eqtime
284
+ if time_utc_minutes < -720.0:
285
+ time_utc_minutes += 1440
286
+
287
+ base_dt = datetime.datetime(date.year, date.month, date.day, tzinfo=datetime.timezone.utc)
288
+ result_utc = base_dt + datetime.timedelta(minutes=time_utc_minutes)
289
+ return result_utc.astimezone(timezone)
290
+
291
+ def dusk(
292
+ latitude: float, longitude: float, date: datetime.date,
293
+ twilight_type: Literal["civil", "nautical", "astronomical"] = "civil",
294
+ elevation: float = 0.0,
295
+ timezone: datetime.timezone = datetime.timezone.utc,
296
+ with_refraction: bool = True
297
+ ) -> Optional[datetime.datetime]:
298
+ """🌙 Calcula anochecer (fin crepúsculo)."""
299
+ zeniths = {"civil": 96.0, "nautical": 102.0, "astronomical": 108.0}
300
+ zenith = zeniths[twilight_type]
301
+
302
+ adjustment_for_elevation = adjust_to_horizon(elevation)
303
+ adjusted_zenith = zenith + adjustment_for_elevation
304
+ if with_refraction:
305
+ adjusted_zenith += refraction_at_zenith(adjusted_zenith)
306
+
307
+ latitude = max(min(latitude, 89.8), -89.8)
308
+ jd = julianday(date)
309
+ jc = julianday_to_juliancentury(jd)
310
+ declination = sun_declination(jc)
311
+ eqtime = eq_of_time(jc)
312
+
313
+ try:
314
+ ha_setting = hour_angle(latitude, declination, adjusted_zenith, "setting")
315
+ except ValueError:
316
+ return None
317
+
318
+ delta = -longitude - degrees(ha_setting)
319
+ time_utc_minutes = 720.0 + (delta * 4.0) - eqtime
320
+ if time_utc_minutes < -720.0:
321
+ time_utc_minutes += 1440
322
+
323
+ base_dt = datetime.datetime(date.year, date.month, date.day, tzinfo=datetime.timezone.utc)
324
+ result_utc = base_dt + datetime.timedelta(minutes=time_utc_minutes)
325
+ return result_utc.astimezone(timezone)
326
+
327
+ def midnight(
328
+ longitude: float,
329
+ date: datetime.date,
330
+ timezone: datetime.timezone = datetime.timezone.utc
331
+ ) -> datetime.datetime:
332
+ """🌑 Medianoche solar (12h después del mediodía solar)."""
333
+ noon_time = noon(longitude, date, timezone)
334
+ return noon_time + datetime.timedelta(hours=12)
335
+
336
+ def sun_position(
337
+ latitude: float,
338
+ longitude: float,
339
+ dt: datetime.datetime,
340
+ elevation: float = 0.0,
341
+ timezone: datetime.timezone = datetime.timezone.utc,
342
+ with_refraction: bool = True
343
+ ) -> dict:
344
+ """
345
+ Calcula la posición del Sol con alta precisión.
346
+ """
347
+ if dt.tzinfo is None:
348
+ dt = dt.replace(tzinfo=timezone)
349
+ dt_utc = dt.astimezone(datetime.timezone.utc)
350
+
351
+ jd = julianday(dt_utc)
352
+ jc = julianday_to_juliancentury(jd)
353
+ declination = sun_declination(jc)
354
+ eqtime = eq_of_time(jc)
355
+
356
+ # Tiempo solar verdadero en minutos desde medianoche
357
+ true_solar_time = (
358
+ dt_utc.hour * 60 + dt_utc.minute + dt_utc.second / 60.0
359
+ + eqtime + 4.0 * longitude
360
+ )
361
+
362
+ # Ángulo horario
363
+ hour_angle = (true_solar_time / 4.0) - 180.0
364
+ if hour_angle < -180:
365
+ hour_angle += 360
366
+ elif hour_angle > 180:
367
+ hour_angle -= 360
368
+
369
+ # Convertir a radianes
370
+ ha_rad = radians(hour_angle)
371
+ lat_rad = radians(latitude)
372
+ dec_rad = radians(declination)
373
+
374
+ # Elevación geométrica
375
+ sin_elev = sin(lat_rad) * sin(dec_rad) + cos(lat_rad) * cos(dec_rad) * cos(ha_rad)
376
+ elevation_geom = degrees(asin(max(min(sin_elev, 1.0), -1.0)))
377
+
378
+ # Corrección por altura del observador
379
+ elevation_adj = elevation_geom + adjust_to_horizon(elevation)
380
+
381
+ # Refracción atmosférica
382
+ if with_refraction and -10 <= elevation_adj <= 90:
383
+ refraction = refraction_at_zenith(90.0 - elevation_adj)
384
+ elevation_final = elevation_adj + refraction
385
+ else:
386
+ elevation_final = elevation_adj
387
+
388
+ # Azimut (0° = Norte)
389
+ sin_elev_final = sin(radians(elevation_final))
390
+ cos_elev_final = cos(radians(elevation_final))
391
+ cos_lat = cos(lat_rad)
392
+ sin_lat = sin(lat_rad)
393
+ sin_dec = sin(dec_rad)
394
+
395
+ if abs(cos_elev_final * cos_lat) < 1e-12:
396
+ azimuth = 180.0 if ha_rad < 0 else 0.0
397
+ else:
398
+ cos_az = (sin_dec - sin_elev_final * sin_lat) / (cos_elev_final * cos_lat)
399
+ cos_az = max(min(cos_az, 1.0), -1.0)
400
+ azimuth = degrees(acos(cos_az))
401
+ if hour_angle > 0: # PM
402
+ azimuth = 360.0 - azimuth
403
+
404
+ horizon_position: Literal["above_horizon", "below_horizon"] = (
405
+ "above_horizon" if elevation_final > 0 else "below_horizon"
406
+ )
407
+ rising = elevation_final > 0 and hour_angle < 0
408
+
409
+ return {
410
+ "elevation": round(elevation_final, 2),
411
+ "azimuth": round(azimuth, 2),
412
+ "horizon_position": horizon_position,
413
+ "rising": rising
414
+ }
415
+
416
+ # 🎁 FUNCIÓN BONUS: Todo junto (opcional)
417
+ def all_sun_events(
418
+ latitude: float, longitude: float, date: datetime.date,
419
+ twilight_type: Literal["civil", "nautical", "astronomical"] = "civil",
420
+ elevation: float = 0.0,
421
+ timezone: datetime.timezone = datetime.timezone.utc,
422
+ with_refraction: bool = True
423
+ ) -> dict:
424
+ """🔥 Calcula TODOS los eventos solares del día."""
425
+ sr, ss = sunrise_sunset(latitude, longitude, date, elevation, timezone, with_refraction)
426
+ return {
427
+ "dawn": dawn(latitude, longitude, date, twilight_type, elevation, timezone, with_refraction),
428
+ "sunrise": sr,
429
+ "noon": noon(longitude, date, timezone),
430
+ "sunset": ss,
431
+ "dusk": dusk(latitude, longitude, date, twilight_type, elevation, timezone, with_refraction),
432
+ "midnight": midnight(longitude, date, timezone)
433
+ }
@@ -0,0 +1,2 @@
1
+ # version.py
2
+ __version__ = "1.0.9"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: solarmoonpy
3
- Version: 1.0.1
3
+ Version: 1.0.9
4
4
  Summary: Precise solar and lunar calculations for astronomical applications
5
5
  Author-email: figorr <jdcuartero@yahoo.es>
6
6
  License: Apache License
@@ -221,6 +221,9 @@ License-File: LICENSE
221
221
  Dynamic: license-file
222
222
 
223
223
  # ☀️🌙 solarmoonpy
224
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
225
+ ![Version](https://img.shields.io/pypi/v/solarmoonpy)
226
+
224
227
  Solar and moon calculations for Meteocat Home Assistant integration.
225
228
 
226
229
  **solarmoonpy** is a Python library that provides accurate calculations of solar and lunar positions, specifically designed to integrate with Home Assistant and Meteocat data.
@@ -229,7 +232,7 @@ This project enables you to obtain information such as sunrise, sunset, moon pha
229
232
 
230
233
  ## 🚀 Features
231
234
 
232
- - ☀️ Solar position calculations (sunrise, sunset, zenith, etc.).
235
+ - ☀️ Solar position calculations (sunrise, sunset, noon, etc.).
233
236
  - 🌙 Moon phase and position calculations.
234
237
  - Integration with meteorological data from Meteocat.
235
238
  - Compatible with Home Assistant for automations based on solar and lunar events.
@@ -1,2 +0,0 @@
1
- # version.py
2
- __version__ = "1.0.1"
File without changes
File without changes