Skip to content

Commit c80016c

Browse files
authored
Merge pull request #163 from aaronwmorris/dev
Add MQTT publish function
2 parents cf33801 + 59d6122 commit c80016c

33 files changed

Lines changed: 1233 additions & 242 deletions

README.md

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ indi-allsky fully automates the capture and processing of master dark calibratio
106106
https://github.com/aaronwmorris/indi-allsky/wiki/Dark-Calibration-Frames
107107

108108

109+
### Moon mode
110+
111+
This is a special night time operating mode intended to reduce gain when the moon is more illuminated and above the horizon
112+
113+
109114
## Keograms
110115
Keograms are a visual representation of the entire timelapse video in a single frame. Every image is rotated so that the vertical aligns to the meridian and then the center-vertical column is extraced from each frame and compiled into the keogram. The rotation parameter in the config is KEOGRAM_ANGLE
111116

@@ -223,8 +228,8 @@ All configuration is read from /etc/indi-allsky/config.json . You can find conf
223228
| LOCATION_LATITUDE | | (float) Your latitude for astrometric calculations |
224229
| LOCATION_LONGITUDE | | (float) Your longitude for astrometric calculations |
225230
| TIMELAPSE_ENABLE | true | (bool) Enable timelapse, keogram, and star trail creation |
226-
| DAYTIME_CAPTURE | false | (bool) Perform day time image capture |
227-
| DAYTIME_TIMELAPSE | false | (bool) Generate timelapse from day time images |
231+
| DAYTIME_CAPTURE | true | (bool) Perform day time image capture |
232+
| DAYTIME_TIMELAPSE | true | (bool) Generate timelapse from day time images |
228233
| DAYTIME_CONTRAST_ENHANCE | false | (bool) Perform CLAHE contrast enhancement on day time images |
229234
| NIGHT_CONTRAST_ENHANCE | false | (bool) Perform CLAHE contrast enhancement on night time images |
230235
| NIGHT_SUN_ALT_DEG | -6 | (degrees) Altitude of Sun to calculate beginning and end of night |
@@ -272,21 +277,27 @@ All configuration is read from /etc/indi-allsky/config.json . You can find conf
272277
| > PASSWORD | | (str) Password for file tranfer |
273278
| > TIMEOUT | 5.0 | (float) Timeout for file transfer before failing |
274279
| > REMOTE_IMAGE_NAME | latest.{0} | (str) Python template for remote file name of latest image, extension is automatically selected from IMAGE_FILE_TYPE |
275-
| REMOTE_IMAGE_FOLDER | | (str) Remote folder to upload latest image |
276-
| REMOTE_VIDEO_FOLDER | | (str) Remote folder to upload time lapse videos |
277-
| REMOTE_KEOGRAM_FOLDER | | (str) Remote folder to upload keograms |
278-
| REMOTE_STARTRAIL_FOLDER | | (str) Remote folder to upload star trails |
279-
| REMOTE_ENDOFNIGHT_FOLDER | | (str) Remote folder to upload Allsky EndOfNight data |
280-
| UPLOAD_IMAGE | 0 | (int) Upload latest image every X frames |
281-
| UPLOAD_VIDEO | false | (bool) Enable timelapse video uploads |
282-
| UPLOAD_KEOGRAM | false | (bool) Enable keogram uploads |
283-
| UPLOAD_STARTRAIL | false | (bool) Enable star trail upload |
284-
| UPLOAD_ENDOFNIGHT | false | (bool) Enable EndOfNight data upload. This is the data.json file for https://github.com/thomasjacquin/allsky-website |
285-
286-
### Moon mode
287-
288-
This is a special night time operating mode intended to reduce gain when the moon is more illuminated and above the horizon
289-
280+
| > REMOTE_IMAGE_FOLDER | | (str) Remote folder to upload latest image |
281+
| > REMOTE_VIDEO_FOLDER | | (str) Remote folder to upload time lapse videos |
282+
| > REMOTE_KEOGRAM_FOLDER | | (str) Remote folder to upload keograms |
283+
| > REMOTE_STARTRAIL_FOLDER | | (str) Remote folder to upload star trails |
284+
| > REMOTE_ENDOFNIGHT_FOLDER | | (str) Remote folder to upload Allsky EndOfNight data |
285+
| > UPLOAD_IMAGE | 0 | (int) Upload latest image every X frames |
286+
| > UPLOAD_VIDEO | false | (bool) Enable timelapse video uploads |
287+
| > UPLOAD_KEOGRAM | false | (bool) Enable keogram uploads |
288+
| > UPLOAD_STARTRAIL | false | (bool) Enable star trail upload |
289+
| > UPLOAD_ENDOFNIGHT | false | (bool) Enable EndOfNight data upload. This is the data.json file for https://github.com/thomasjacquin/allsky-website |
290+
| MQTTPUBLISH | | (dict) MQTT configuration |
291+
| > ENABLE | false | (bool) Enable MQTT publishing |
292+
| > TRANSPORT | tcp | (str) MQTT Transport - tcp or websockets |
293+
| > HOST | | (str) MQTT/Mosquitto server |
294+
| > PORT | 8883 | (int) MQTT port, 1883 = standard, 8883 = TLS |
295+
| > USERNAME | | (str) MQTT user |
296+
| > PASSWORD | | (str) MQTT password |
297+
| > BASE_TOPIC | indi-allsky | (str) Base topic for MQ messages |
298+
| > QOS | 0 | (int) MQTT QoS for messages |
299+
| > TLS | true | (bool) Use TLS for MQTT connection |
300+
| > CERT_BYPASS | true | (bool) Bypass certificate validation for MQTT connection |
290301

291302
## Tested Hardware
292303

config.json_template

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@
9999

100100
"ORB_PROPERTIES" : {
101101
"RADIUS" : 9,
102-
"SUN_COLOR" : [0, 255, 255],
103-
"MOON_COLOR" : [255, 255, 255]
102+
"SUN_COLOR" : [255, 255, 255],
103+
"MOON_COLOR" : [128, 128, 128]
104104
},
105105

106106
"FILETRANSFER" : {
@@ -123,5 +123,21 @@
123123
"UPLOAD_KEOGRAM" : false,
124124
"UPLOAD_STARTRAIL" : false,
125125
"UPLOAD_ENDOFNIGHT" : false
126+
},
127+
128+
"MQTTPUBLISH" : {
129+
"ENABLE" : false,
130+
"comment_TRANSPORT" : "tcp or websockets",
131+
"TRANSPORT" : "tcp",
132+
"HOST" : "localhost",
133+
"comment_PORT" : "1883 = mqtt, 8883 = TLS",
134+
"PORT" : 8883,
135+
"USERNAME" : "indi-allsky",
136+
"PASSWORD" : "",
137+
"BASE_TOPIC" : "indi-allsky",
138+
"comment_QOS" : "0, 1, or 2",
139+
"QOS" : 0,
140+
"TLS" : true,
141+
"CERT_BYPASS" : true
126142
}
127143
}

indi_allsky/allsky.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def __init__(self, f_config_file):
5959
self.sensortemp_v = Value('f', 0)
6060
self.night_v = Value('i', -1) # bogus initial value
6161
self.night = None
62-
self.moonmode_v = Value('f', 0.0) # contains moon phase %
62+
self.moonmode_v = Value('f', 0.0) # contains moon phase % if moonmode
6363
self.moonmode = None
6464

6565
self.night_sun_radians = math.radians(self.config['NIGHT_SUN_ALT_DEG'])

indi_allsky/filetransfer/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .pycurl_ftps import ftps
1010
from .pycurl_ftpes import pycurl_ftpes
1111
from .pycurl_ftpes import ftpes
12+
from .paho_mqtt import paho_mqtt
1213

1314
__all__ = (
1415
'paramiko_sftp',
@@ -21,4 +22,5 @@
2122
'ftp',
2223
'ftps',
2324
'ftpes',
25+
'paho_mqtt',
2426
)
Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,50 @@
1+
#from pathlib import Path
12
import logging
23

34
logger = logging.getLogger('indi_allsky')
45

56

67
class GenericFileTransfer(object):
7-
def __init__(self, timeout=5.0):
8-
self.timeout = float(timeout)
8+
def __init__(self):
99

10-
self.port = 0
11-
self.client = None
10+
self._port = 0
11+
self._timeout = 5.0
1212

13+
self._client = None
14+
15+
16+
@property
17+
def port(self):
18+
return self._port
19+
20+
@port.setter
21+
def port(self, new_port):
22+
self._port = int(new_port)
1323

14-
def __del__(self):
15-
pass
1624

25+
@property
26+
def timeout(self):
27+
return self._timeout
1728

18-
def connect(self, hostname, username, password, port=None):
19-
if port:
20-
logger.info('Port override to %d', port)
21-
self.port = port
29+
@timeout.setter
30+
def timeout(self, new_timeout):
31+
self._timeout = float(new_timeout)
2232

23-
logger.info('Connecting to %s as %s with %s', hostname, username, self.__class__.__name__)
24-
self.client = self._connect(hostname, username, password)
33+
34+
def connect(self, *args, **kwargs):
35+
hostname = kwargs['hostname']
36+
username = kwargs['username']
37+
#password = kwargs['password']
38+
39+
logger.info('Connecting to %s:%d as %s with %s', hostname, self._port, username, self.__class__.__name__)
2540

2641

2742
def close(self):
28-
self._close()
43+
pass
44+
2945

46+
def put(self, *args, **kwargs):
47+
local_file = kwargs['local_file']
3048

31-
def put(self, localfile, remotefile):
32-
logger.info('Uploading %s to %s', localfile, remotefile)
33-
self._put(localfile, remotefile)
49+
logger.info('Uploading %s', local_file)
3450

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
from .generic import GenericFileTransfer
2+
from .exceptions import AuthenticationFailure
3+
from .exceptions import ConnectionFailure
4+
#from .exceptions import TransferFailure
5+
6+
from pathlib import Path
7+
import paho.mqtt.publish as publish
8+
from paho.mqtt import MQTTException
9+
import ssl
10+
import io
11+
import socket
12+
import time
13+
import logging
14+
15+
logger = logging.getLogger('indi_allsky')
16+
17+
18+
class paho_mqtt(GenericFileTransfer):
19+
def __init__(self, *args, **kwargs):
20+
super(paho_mqtt, self).__init__(*args, **kwargs)
21+
22+
self._port = 1883
23+
24+
self.mq_transport = None
25+
self.mq_hostname = None
26+
self.mq_auth = None
27+
self.mq_tls = None
28+
29+
30+
def connect(self, *args, **kwargs):
31+
super(paho_mqtt, self).connect(*args, **kwargs)
32+
33+
transport = kwargs['transport']
34+
hostname = kwargs['hostname']
35+
username = kwargs['username']
36+
password = kwargs.get('password') if kwargs.get('password') else None
37+
tls = kwargs.get('tls')
38+
cert_bypass = kwargs.get('cert_bypass')
39+
40+
41+
self.mq_transport = transport
42+
self.mq_hostname = hostname
43+
44+
if tls:
45+
self.mq_tls = {
46+
'ca_certs' : '/etc/ssl/certs/ca-certificates.crt',
47+
'cert_reqs' : ssl.CERT_REQUIRED,
48+
'insecure' : False,
49+
}
50+
51+
if cert_bypass:
52+
self.mq_tls['cert_reqs'] = ssl.CERT_NONE
53+
self.mq_tls['insecure'] = True
54+
55+
56+
57+
if username:
58+
self.mq_auth = {
59+
'username' : username,
60+
'password' : password,
61+
}
62+
63+
64+
def close(self):
65+
super(paho_mqtt, self).close()
66+
67+
68+
def put(self, *args, **kwargs):
69+
super(paho_mqtt, self).put(*args, **kwargs)
70+
71+
local_file = kwargs['local_file']
72+
base_topic = kwargs['base_topic']
73+
qos = kwargs['qos']
74+
mq_data = kwargs['mq_data']
75+
76+
local_file_p = Path(local_file)
77+
78+
79+
message_list = list()
80+
81+
# publish image
82+
with io.open(local_file_p, 'rb') as f_localfile:
83+
image_data = f_localfile.read()
84+
message_list.append({
85+
'topic' : '/'.join((base_topic, 'latest')),
86+
'payload' : bytearray(image_data),
87+
'qos' : qos,
88+
'retain' : True,
89+
})
90+
91+
92+
for k, v in mq_data.items():
93+
message_list.append({
94+
'topic' : '/'.join((base_topic, k)),
95+
'payload' : v,
96+
'qos' : qos,
97+
'retain' : True,
98+
})
99+
100+
101+
start = time.time()
102+
103+
try:
104+
publish.multiple(
105+
message_list,
106+
transport=self.mq_transport,
107+
hostname=self.mq_hostname,
108+
port=self._port,
109+
client_id='',
110+
keepalive=60,
111+
auth=self.mq_auth,
112+
tls=self.mq_tls,
113+
)
114+
except socket.gaierror as e:
115+
raise ConnectionFailure(str(e)) from e
116+
except socket.timeout as e:
117+
raise ConnectionFailure(str(e)) from e
118+
except ssl.SSLCertVerificationError as e:
119+
raise ConnectionFailure(str(e)) from e
120+
except ConnectionRefusedError as e:
121+
raise ConnectionFailure(str(e)) from e
122+
except MQTTException as e:
123+
raise AuthenticationFailure(str(e)) from e
124+
125+
upload_elapsed_s = time.time() - start
126+
local_file_size = local_file_p.stat().st_size
127+
logger.info('File transferred in %0.4f s (%0.2f kB/s)', upload_elapsed_s, local_file_size / upload_elapsed_s / 1024)
128+
129+

0 commit comments

Comments
 (0)