astropanel 2.2 → 2.5
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.
- checksums.yaml +4 -4
- data/bin/astropanel +100 -60
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba68a8b88ebc1705f759ebf2b93bb0bfc8f13a0fc3ee75b8223e0c0f46f7e0d5
|
4
|
+
data.tar.gz: 7cd712f5bbc7951d13c6b6f1394984950bc732970ce593b21a2d78d1d94f4ad4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 072c8766c42c619a0999c8aa715bfc35e696f3798c1db3f0124c76b8f3531059369c0826dde7b42745ba7c7dbf925c9edfddc543ad68f1e2301bc6146955b784
|
7
|
+
data.tar.gz: 70cb74c006fc864713f93f67615339fef2cc90f8e4776b355797c79ea80bd0fd66feb02d4357b1f643025fa31cfde99df468337590cf900d287a383fcc62b069
|
data/bin/astropanel
CHANGED
@@ -14,7 +14,7 @@
|
|
14
14
|
# for any damages resulting from its use. Further, I am under no
|
15
15
|
# obligation to maintain or extend this software. It is provided
|
16
16
|
# on an 'as is' basis without any expressed or implied warranty.
|
17
|
-
# Version: 2.
|
17
|
+
# Version: 2.5: Bug fix
|
18
18
|
|
19
19
|
# LOAD MODULES {{{1
|
20
20
|
require 'io/console'
|
@@ -31,9 +31,6 @@ include Rcurses::Input
|
|
31
31
|
include Rcurses::Cursor
|
32
32
|
|
33
33
|
# BASIC SETUP {{{1
|
34
|
-
$stdin.raw!
|
35
|
-
$stdin.echo = false
|
36
|
-
|
37
34
|
CONFIG_FILE = File.join(Dir.home, '.ap.conf')
|
38
35
|
|
39
36
|
# CLASS EXTENSIONS {{{1
|
@@ -56,13 +53,6 @@ class String # {{{2
|
|
56
53
|
end
|
57
54
|
end
|
58
55
|
|
59
|
-
# AT_EXIT {{{1
|
60
|
-
at_exit do
|
61
|
-
$stdin.cooked!
|
62
|
-
$stdin.echo = true
|
63
|
-
Rcurses.clear_screen
|
64
|
-
Cursor.show
|
65
|
-
end
|
66
56
|
|
67
57
|
# EPHEMERIS CORE {{{1
|
68
58
|
class Ephemeris
|
@@ -728,27 +718,40 @@ class AstroPanelApp
|
|
728
718
|
@header.say(txt.ljust(cols))
|
729
719
|
end
|
730
720
|
|
731
|
-
|
721
|
+
# Update the top‐bar title line, safely handling missing planet data
|
722
|
+
def update_titles
|
732
723
|
_, cols = IO.console.winsize
|
733
724
|
|
734
|
-
|
735
|
-
|
725
|
+
# Base limits display
|
726
|
+
title = " …#{@cloudlimit}% …#{@humiditylimit}% " \
|
727
|
+
"#{@templimit}°C… …#{@windlimit}m/s".fg(244)
|
736
728
|
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
729
|
+
# Current date/hour
|
730
|
+
if @weather[@index]
|
731
|
+
date = @weather[@index][:date]
|
732
|
+
hr = @weather[@index][:hour]
|
733
|
+
col = cond_color(@index)
|
734
|
+
else
|
735
|
+
date = nil
|
736
|
+
hr = nil
|
737
|
+
col = 244
|
738
|
+
end
|
743
739
|
|
744
740
|
title << ' ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ '.fg(244)
|
745
|
-
title << "#{date} (#{Date.parse(date).strftime("%A")}) #{hr}:00".b.fg(col)
|
746
|
-
title << " Moon: #{mp_name} (#{mp}%)".fg(244)
|
741
|
+
title << "#{date}#{ date ? " (#{Date.parse(date).strftime("%A")}) #{hr}:00" : ""}".b.fg(col)
|
747
742
|
|
743
|
+
# Safely fetch moon‐phase info (mphase/mph_s) if it exists
|
744
|
+
if date && (pd = @planets[date])
|
745
|
+
mp, mp_name = pd.values_at(:mphase, :mph_s)
|
746
|
+
title << " Moon: #{mp_name} (#{mp}%)".fg(244)
|
747
|
+
else
|
748
|
+
title << " Moon: unavailable".fg(244)
|
749
|
+
end
|
750
|
+
|
751
|
+
@titles.clear
|
748
752
|
@titles.say(title.ljust(cols))
|
749
753
|
end
|
750
754
|
|
751
|
-
|
752
755
|
def update_footer # {{{2
|
753
756
|
_, cols = IO.console.winsize
|
754
757
|
cmds = "?=Help l=Loc a=Lat o=Lon c=Cloud h=Hum t=Temp w=Wind b=Bortle " \
|
@@ -757,71 +760,85 @@ class AstroPanelApp
|
|
757
760
|
@footer.say(cmds.ljust(cols))
|
758
761
|
end
|
759
762
|
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
763
|
+
# Redraw the left‐pane list of weather + visibility blocks,
|
764
|
+
# Redraw the left‐pane list of weather + visibility blocks,
|
765
|
+
# safely handling missing planet data and converting rise/set to integers
|
766
|
+
def update_left
|
767
|
+
date_o = ''
|
768
|
+
date_n = ''
|
769
|
+
|
770
|
+
all_lines = @weather.each_with_index.map do |w, i|
|
771
|
+
col = cond_color(i)
|
772
|
+
|
773
|
+
# Safely fetch planet data for this date (may be nil)
|
774
|
+
pd = @planets[w[:date]] || {}
|
775
|
+
mp = pd[:mphase]
|
776
|
+
|
777
|
+
# Prepare date display, only show the date when it changes
|
778
|
+
date_o = date_n
|
779
|
+
date_n = w[:date]
|
780
|
+
date_s = (date_n == date_o ? ' '.ljust(10) : date_n)
|
781
|
+
|
782
|
+
# Fixed‐width weather columns
|
783
|
+
hour_s = w[:hour]
|
772
784
|
cloud_s = "#{w[:cloud]}%".rjust(4)
|
773
785
|
hum_s = "#{w[:humidity]}%".rjust(5)
|
774
786
|
tmp_s = "#{w[:temp]}°C".rjust(6)
|
775
787
|
wnd_s = "#{w[:wind]}(#{w[:wdir]})".rjust(8)
|
776
788
|
|
777
|
-
#
|
789
|
+
# Build the left‐pane row
|
778
790
|
row = "#{date_s} ".fg(col)
|
779
791
|
ul = "#{hour_s} #{cloud_s} #{hum_s} #{tmp_s} #{wnd_s}".fg(col)
|
780
792
|
ul = ul.u if i == @index
|
781
793
|
row += ul
|
782
794
|
|
783
|
-
#
|
795
|
+
# Event marker
|
784
796
|
if ev = @events[w[:date]] and ev[:time][0..1] == w[:hour]
|
785
797
|
row += " !".fg(col)
|
786
798
|
else
|
787
799
|
row += " "
|
788
800
|
end
|
789
801
|
|
790
|
-
#
|
791
|
-
BODIES.each_with_index do |b,
|
792
|
-
rise
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
802
|
+
# Visibility blocks for Sun, Moon & planets
|
803
|
+
BODIES.each_with_index do |b, j|
|
804
|
+
# Extract just the hour component from rise/set, convert to Integer
|
805
|
+
rise_h = pd[:"#{b}_rise"] ? pd[:"#{b}_rise"][0..1].to_i : nil
|
806
|
+
set_h = pd[:"#{b}_set"] ? pd[:"#{b}_set"][0..1].to_i : nil
|
807
|
+
hr = w[:hour].to_i
|
808
|
+
|
809
|
+
above = if rise_h.nil? || set_h.nil?
|
810
|
+
false
|
811
|
+
elsif rise_h > set_h
|
812
|
+
(hr >= rise_h) || (hr <= set_h)
|
813
|
+
else
|
814
|
+
(hr >= rise_h) && (hr <= set_h)
|
815
|
+
end
|
816
|
+
|
817
|
+
# Choose block character and color
|
818
|
+
block_char = (0..1) === j ? '█' : '┃'
|
819
|
+
if above
|
820
|
+
color = (b == 'moon' ? moon_phase_color(mp) : BODY_COLORS[b])
|
821
|
+
block = block_char.fg(color)
|
800
822
|
else
|
801
|
-
|
823
|
+
block = ' '
|
802
824
|
end
|
803
|
-
|
825
|
+
|
804
826
|
row << " #{block}"
|
805
827
|
end
|
806
828
|
|
807
829
|
row
|
808
830
|
end
|
809
831
|
|
810
|
-
#
|
832
|
+
# Write into the pane buffer and center the selection
|
811
833
|
@left.text = all_lines.join("\n")
|
834
|
+
height = @left.h
|
812
835
|
|
813
|
-
# figure out how many rows we can show
|
814
|
-
height = @left.h
|
815
|
-
|
816
|
-
# center the selection (3-row “scroll‐off”)…
|
817
836
|
top = @index - (height / 2)
|
818
837
|
top = 0 if top < 0
|
819
838
|
max_top = all_lines.size - height
|
820
839
|
top = max_top if top > max_top
|
821
840
|
|
822
841
|
@left.ix = top
|
823
|
-
|
824
|
-
# redraw just the left pane
|
825
842
|
@left.refresh
|
826
843
|
end
|
827
844
|
|
@@ -913,12 +930,24 @@ class AstroPanelApp
|
|
913
930
|
update_main
|
914
931
|
end
|
915
932
|
|
916
|
-
|
933
|
+
# Fetch weather from met.no, but never let network errors crash the app
|
934
|
+
def get_weather
|
917
935
|
uri = URI("https://api.met.no/weatherapi/locationforecast/2.0/complete?lat=#{@lat}&lon=#{@lon}")
|
918
936
|
req = Net::HTTP::Get.new(uri)
|
919
937
|
req['User-Agent'] = 'astropanel/1.0 g@isene.com'
|
920
|
-
|
921
|
-
|
938
|
+
|
939
|
+
begin
|
940
|
+
res = Net::HTTP.start(uri.hostname, uri.port,
|
941
|
+
use_ssl: true,
|
942
|
+
read_timeout: 10) do |http|
|
943
|
+
http.request(req)
|
944
|
+
end
|
945
|
+
rescue SocketError, Socket::ResolutionError, Timeout::Error, Net::OpenTimeout => e
|
946
|
+
@footer.say("⚠️ Weather fetch failed: #{e.class}: #{e.message}")
|
947
|
+
@weather = []
|
948
|
+
return
|
949
|
+
end
|
950
|
+
|
922
951
|
if res.is_a?(Net::HTTPSuccess)
|
923
952
|
series = JSON.parse(res.body)
|
924
953
|
.dig('properties','timeseries') || []
|
@@ -939,7 +968,7 @@ class AstroPanelApp
|
|
939
968
|
}
|
940
969
|
end
|
941
970
|
else
|
942
|
-
@footer.say("Weather API error: #{res.code} #{res.message}")
|
971
|
+
@footer.say("⚠️ Weather API error: #{res.code} #{res.message}")
|
943
972
|
@weather = []
|
944
973
|
end
|
945
974
|
end
|
@@ -1043,6 +1072,17 @@ class AstroPanelApp
|
|
1043
1072
|
end
|
1044
1073
|
end
|
1045
1074
|
|
1075
|
+
# START PROGRAM {{{1
|
1076
|
+
begin
|
1077
|
+
# a single quick DNS + TCP check
|
1078
|
+
TCPSocket.new('api.met.no', 443).close
|
1079
|
+
rescue SocketError, Socket::ResolutionError => e
|
1080
|
+
$stderr.puts "FATAL: can’t resolve api.met.no – network appears down (#{e.message})"
|
1081
|
+
$stdin.cooked!
|
1082
|
+
$stdin.echo = true
|
1083
|
+
exit!
|
1084
|
+
end
|
1085
|
+
|
1046
1086
|
AstroPanelApp.new
|
1047
1087
|
|
1048
1088
|
# VIM MODELINE{{{1
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: astropanel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '2.
|
4
|
+
version: '2.5'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Geir Isene
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-05-
|
11
|
+
date: 2025-05-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rcurses
|
@@ -28,8 +28,8 @@ description: 'This program shows essential data in order to plan your observatio
|
|
28
28
|
9 days weather forecast, full ephemeris for the Sun, the Moon and all major planets,
|
29
29
|
complete with graphic representation of rise/set times, detailed info for each day
|
30
30
|
with important astronomical events, star chart displayed in the terminal and more.
|
31
|
-
New in 2.0: Full rewrite using rcurses (https://github.com/isene/rcurses). 2.
|
32
|
-
|
31
|
+
New in 2.0: Full rewrite using rcurses (https://github.com/isene/rcurses). 2.5:
|
32
|
+
Bug fix.'
|
33
33
|
email: g@isene.com
|
34
34
|
executables:
|
35
35
|
- astropanel
|