Browse Source

Ayy finally a full v2.0 release xd Settled on using OneSignal as a push service pr0viturd for weather notifications (including support for specific notification channel and player segment), also added ./temps.py --weathermon flag to force run that shit and some other misc impr0vements [=[=[=[=[=[[=[=[=[=

Wazakindjes 1 year ago
parent
commit
a02b1930d3
3 changed files with 79 additions and 41 deletions
  1. 3 1
      README.md
  2. 12 6
      muhconf.ini.example
  3. 64 34
      temps.py

+ 3 - 1
README.md

@@ -10,10 +10,12 @@ You prolly need to install a few Pythinz modules cuz they may not be available b
 * `datetime`
 * `json`
 * `MySQLdb`
+* `onesignal`
 * `os`
 * `requests`
 * `sys`
 * `time`
+* `var_dump` (for printing object properties etc ;])
 
 # Installation
 Copy `muhconf.ini.example` to `muhconf.ini` and fire up een editor. All the options are self-explanatory or explained in there so git to reading fam. The config file must be kept in the same dir as the skrip.
@@ -44,4 +46,4 @@ For checkin' em outdoors temperature I'm using an API from OpenWeatherMap, which
 OWP supports Celsius, Fahrenheit and Kelvin as temperature units, but the sensor always uses Celsius afaik so we'll force OWP to work with that too, deal w/ it. ;]
 
 ### Notification shit
-__For now I'm currently using a demo application myself but I'm working on converting it to the style of mein other apps and will publish em at that time. ;];];];];]];;];]__
+Now, in order to actually __receive__ the notifications I made a [qt lil Andr0id app](https://gitgud.malvager.net/Wazakindjes/Pushem) to receive push notifications from multiple sources. =] Just refer to that shit to read how to set em up and make the necessary config changes for `temps.py`. ;];];];]

+ 12 - 6
muhconf.ini.example

@@ -44,16 +44,22 @@ tls = false
 # Interval is in minutes and is *not* relative to the skrip's startup time, rather it simply checks if the current hour to minutes + current minutes is evenly divisible by the interval
 # Therefore it can't be more than 60 and it should be evenly divisible by the time used in your crontab as well ;]
 # Free weather APIs generally don't update faster than once every 10 minutes, but you should checkem at least once per hour anyways
-#interval = 20
+#interval = 10
 
 #weather_url = https://api.openweathermap.org/data/2.5/weather
 #weather_key = 11a11a111a1a11a111111111aa1a1aaa
 #city_id = 123456789
 
-#pushy_url = https://api.pushy.me
-#pushy_key = 1a11aa1a11aa1a11aa1aa1aaa11aaa1a1a111aa1a111a111a11111111a1a1111
-#device_id = aaa11111111a111111111a
-
-# TLS certificate bundle file used by the requests module
+# TLS certificate bundle file used by the requests module for em weather API
 # This shit is optional and defaults to the below value
 #tls_ca_file = /etc/ssl/certs/ca-certificates.crt
+
+# Copy both app ID and REST API key from your OneSignal dashboard > app's settings > Keys & IDs
+#onesignal_restkey = aaaaaaa1aaaaaaa1aa11aaaaaaaaaaaaaaaaaaaaaaaaaaaa
+#onesignal_appid = a11a11a1-aa11-1aa1-a11a-1a11a11a1aaa
+
+# Also copy the notification category's channel ID from your OneSignal dashboard > app's settings > Messaging
+#onesignal_channel = a11a11a1-aa11-1aa1-a11a-1a11a11a1aaa
+
+# And finally, the segment of subscribers to send to (optional, defaults to everyone if not specified)
+#onesignal_segment = temp_sensor

+ 64 - 34
temps.py

@@ -8,10 +8,12 @@ import ConfigParser
 import datetime
 import json
 import MySQLdb
+from onesignal import OneSignal
 import os
 import requests
 import sys
 import time
+from var_dump import var_dump
 
 # Some "constants" lol
 RET_GUCCI = 0
@@ -23,9 +25,13 @@ RET_ERR_WEATHERMON = 3
 muhconf = {}
 skripdir = os.path.dirname(os.path.realpath(__file__))
 
-def printem(txt):
+def printem(obj):
 	# Because apparently we need to flush after every print to ensure it happens in real-time -.-
-	print txt
+	# Also this waii we can check if the object is properly printable by default, some objects may not be so let's use var_dump() for that shit instead ;]
+	if isinstance(obj, (float, int, str, list, dict, tuple)):
+		print obj
+	else:
+		var_dump(obj)
 	sys.stdout.flush()
 
 def try_db_cleanup(db, cursor):
@@ -71,6 +77,7 @@ def readem_conf():
 			# [weathermon] section
 			"enable": "false",
 			"tls_ca_file": "/etc/ssl/certs/ca-certificates.crt",
+			"onesignal_segment": None,
 			# The other variables (interval, url, key) are not optional if enable == true, so they shouldn't be specified here either ;]
 		})
 		cfg.read("{0}/muhconf.ini".format(skripdir))
@@ -171,14 +178,20 @@ def readem_conf():
 				muhconf["weathermon"][weathermon_intopt] = optval
 
 			# String values last
-			for weathermon_stropt in ["weather_url", "weather_key", "tls_ca_file", "pushy_url", "pushy_key", "device_id"]:
+			for weathermon_stropt in ["weather_url", "weather_key", "tls_ca_file", "onesignal_restkey", "onesignal_appid", "onesignal_channel", "onesignal_segment"]:
 				optval = cfg.get("weathermon", weathermon_stropt)
+
+				# Cuz certain optional vars default to None lol (missing keys throw an exception ;])
+				if optval is None:
+					muhconf["weathermon"][weathermon_stropt] = optval
+					continue
+
 				if len(optval) == 0:
 					printem("Invalid value for option '{0}': may not be left empty".format(weathermon_stropt))
 					err = True
 					continue
 
-				elif weathermon_stropt in ["weather_url", "pushy_url"]:
+				elif weathermon_stropt in ["weather_url"]:
 					if not optval.startswith("https://"):
 						printem("Invalid value '{0}' for option '{1}': must start with https://".format(optval, weathermon_stropt))
 						err = True
@@ -195,7 +208,7 @@ def readem_conf():
 						err = True
 						continue
 
-				muhconf["weathermon"][weathermon_stropt] = cfg.get("weathermon", weathermon_stropt)
+				muhconf["weathermon"][weathermon_stropt] = optval
 		### [weathermon] section END
 
 	except KeyboardInterrupt:
@@ -386,40 +399,47 @@ def checkem_weathermon(inside_temp):
 	if notify:
 		try:
 			if curstate == "outdoors_higher":
-				msg = "Outside temperature is now ab0ve inside temperature"
+				msg = "Ayy outside temperature is now ab0ve inside temperature"
 			else:
-				msg = "Outside temperature is now bel0w inside temperature"
-
-			payload = json.dumps({
-				"to": muhconf["weathermon"]["device_id"],
-				"data": { # General payload
-					"title": "Temperature state changed",
-					"message": msg,
-				},
-				"notification": { # iOS
-					"title": "Temperature state changed",
-					"body": msg,
-					"badge": 1, # Badge number
-					"sound": "ping.aiff",
-				}
-			})
-
-			url = "{0}/push?api_key={1}".format(muhconf["weathermon"]["pushy_url"], muhconf["weathermon"]["pushy_key"])
-			req = requests.post(url, data=payload, headers={"Content-Type": "application/json"}, verify=muhconf["weathermon"]["tls_ca_file"])
+				msg = "Ayy outside temperature is now bel0w inside temperature"
+
+			client = OneSignal(muhconf["weathermon"]["onesignal_restkey"], muhconf["weathermon"]["onesignal_appid"])
+			segments = ['All']
+			if muhconf["weathermon"]["onesignal_segment"] is not None:
+				segments = [muhconf["weathermon"]["onesignal_segment"]]
+
+			req = client.create_notification(
+				# Arguments used by the library
+				contents=msg,
+				heading="Temperature state changed",
+				url='',
+				included_segments=segments,
+
+				# These are already the defaults but putting that shit here for reference kek
+				#app_id=None,
+				#player_ids=None, # List or tuple, takes precedence over included_segments
+
+				# Custom args to pass directly to the API
+				android_channel_id=muhconf["weathermon"]["onesignal_channel"],
+				huawei_channel_id=muhconf["weathermon"]["onesignal_channel"], # Is apparently a separate thing lmao
+			)
 
 			if not req.text.startswith("{"):
 				printem("Unable to send notification: received invalid JSON response")
 				printem("\t{0}".format(req.text))
 				sys.exit(RET_ERR_WEATHERMON)
 
+			notification = req.json()
+
 			if req.status_code != 200:
 				printem("Unable to send notification: received {0} HTTP status code instead of 200".format(req.status_code))
 				printem("\t{0}".format(req.text))
 				sys.exit(RET_ERR_WEATHERMON)
 
-			notification = req.json()
-			if "error" in notification.keys():
-				printem("Unable to send notification: [{0}] {1}".format(notification["code"], notification["error"]))
+			if "errors" in notification and len(notification["errors"]):
+				printem("Unable to send notification:")
+				for err in notification["errors"]:
+					printem("\t{0}".format(err))
 				sys.exit(RET_ERR_WEATHERMON)
 
 			if muhconf["main"]["debug"]:
@@ -436,6 +456,7 @@ def checkem_weathermon(inside_temp):
 if __name__ == "__main__":
 	dt = datetime.datetime.now()
 	weathermon_mins = (dt.hour * 60) + dt.minute
+	force_weathermon = False
 
 	# Read config first lol
 	readem_conf()
@@ -444,6 +465,7 @@ if __name__ == "__main__":
 	p = argparse.ArgumentParser()
 	p.add_argument("--debug", help="force debug mode (current setting from config: {0})".format(muhconf["main"]["debug"]), action="store_true")
 	p.add_argument("--dryrun", help="do almost everything as usual (connect to SQL, read temperature) but don't actually insert into the database (current setting from config: {0})".format(muhconf["main"]["dryrun"]), action="store_true")
+	p.add_argument("--weathermon", help="force running weathermon (current interval from config: {0})".format(muhconf["weathermon"]["interval"]), action="store_true")
 	p.add_argument("--version", help="print version and exit lol", action="store_true")
 	args, noargs = p.parse_known_args() # noargs contains the 'topkek' in: ./temps.py --debug topkek
 
@@ -457,6 +479,9 @@ if __name__ == "__main__":
 	if args.dryrun:
 		muhconf["main"]["dryrun"] = True
 
+	if args.weathermon:
+		force_weathermon = True
+
 	if muhconf["main"]["debug"]:
 		printem("Reading em temp")
 	avgtemperatures = readem_temp()
@@ -466,16 +491,21 @@ if __name__ == "__main__":
 			printem("Wanted to run weathermon but avgtemperatures was unexpectedly empty lol")
 			sys.exit(RET_ERR_WEATHERMON)
 
-		weathermon_remaining = weathermon_mins % muhconf["weathermon"]["interval"]
-		if muhconf["main"]["debug"]:
-			printem("Checking if we need to run weathermon: {0} % {1} = {2}".format(weathermon_mins, muhconf["weathermon"]["interval"], weathermon_remaining))
-
-		if weathermon_remaining == 0:
+		if force_weathermon:
 			if muhconf["main"]["debug"]:
-				printem("Ayy running weathermon fam")
+				printem("Ayy forced to run weathermon fam")
 			checkem_weathermon(avgtemperatures[0])
 		else:
+			weathermon_remaining = weathermon_mins % muhconf["weathermon"]["interval"]
 			if muhconf["main"]["debug"]:
-				printem("Not running weathermon fam")
+				printem("Checking if we need to run weathermon: {0} % {1} = {2}".format(weathermon_mins, muhconf["weathermon"]["interval"], weathermon_remaining))
+
+			if weathermon_remaining == 0:
+				if muhconf["main"]["debug"]:
+					printem("Ayy running weathermon fam")
+				checkem_weathermon(avgtemperatures[0])
+			else:
+				if muhconf["main"]["debug"]:
+					printem("Not running weathermon fam")
 
 	sys.exit(RET_GUCCI) # Let's do an explicit exit w/ code imo tbh famlamlaml