Tempest Weather System
Posted: Friday 24 July 2020 9:41
Anyone know if this one is possible to get working with Domoticz other than IFTTT? Looks https://weatherflow.com/tempest-weather-system/
Tempest is responding to a API call in JSON so it is not very difficult to get the data to display in domoticz sensors. Can be done via
Code: Select all
#!/usr/bin/python
# -*- coding = utf-8 to enable reading by simple editors -*-
# (c)2020 script compiled by Toulon7559 from various material from forums
# Version 00_201206, reading JSON, setting Virtual Devices in Domoticz
#
# For JSON-extraction required to have Station_ID [= number of 5 digits] and AccessToken
# as per https://weatherflow.github.io/Tempest/api/
# --------------------------------------------------
# Line009 = PREPARATION & SETTING
# --------------------------------------------------
# Imports for script-operation
import json
import urllib
print ('Start of script Tempest00')
# --------------------------------------------------
# Line016 = EXTRACT CONTENTs BY JSON-CALL to the Weatherflow_Server
# --------------------------------------------------
# General call-string = https://swd.weatherflow.com/swd/rest/observations/station/[your_station_id]?token=[your_access_token]
# Below fill-in is for illustration with fake id+token!
page = urllib.urlopen('https://swd.weatherflow.com/swd/rest/observations/station/12345?token=a1b2c3d4e5f6g7h8i9')
content_test = page.read()
obj_test = json.loads(content_test)
print(obj_test)
# --------------------------------------------------
# Line025 = Extraction & Setting of components
# --------------------------------------------------
BRIGHT = obj_test['obs'][0]['brightness']
PRESST = obj_test['obs'][0]['pressure_trend']
BLITZ0 = obj_test['obs'][0]['lightning_strike_count']
BLITZ1 = obj_test['obs'][0]['lightning_strike_count_last_1hr']
BLITZ3 = obj_test['obs'][0]['lightning_strike_count_last_3hr']
# -------------------------------------------------
# Line032 = Upload to Virtual Devices at Domoticz1
# -------------------------------------------------
# JSON-calls for update:
# Destination device type = Lux => /json.htm?type=command¶m=udevice&idx=IDX&svalue=VALUE with VALUE = value of luminosity in Lux (INTEGER/FLOAT)
# Destination device type = Text => /json.htm?type=command¶m=udevice&idx=IDX&nvalue=0&svalue=TEXT with TEXT = Text you want to display
# Destination device type = Custom => /json.htm?type=command¶m=udevice&idx=IDX&nvalue=0&svalue=VALUE
#domoticz settings
domoticz1_host = '127.0.0.1' # for local host
domoticz1_port = '8080'
domoticz1_url = 'json.htm'
domoticz1_idx1 = '77' #idx of new device Lux, for Brightness
domoticz1_idx2 = '78' #idx of new device Text, for Pressure_trend
domoticz1_idx3 = '79' #idx of new device Custom, for Lightning_Strike_Count
# -------------------------------------------------
# Line046 = UPLOAD to local Domoticz1
# -------------------------------------------------
url = ("http://" + domoticz1_host + ":" + domoticz1_port + "/" + domoticz1_url+ "?type=command¶m=udevice&idx=" + domoticz1_idx1 + "&nvalue=0&svalue=" + str(BRIGHT))
urllib.urlopen(url)
url = ("http://" + domoticz1_host + ":" + domoticz1_port + "/" + domoticz1_url+ "?type=command¶m=udevice&idx=" + domoticz1_idx2 + "&nvalue=0&svalue=" + str(PRESST))
urllib.urlopen(url)
url = ("http://" + domoticz1_host + ":" + domoticz1_port + "/" + domoticz1_url+ "?type=command¶m=udevice&idx=" + domoticz1_idx3 + "&nvalue=0&svalue=" + str(BLITZ0) + ";" + str(BLITZ1) + ";" +str(BLITZ3))
urllib.urlopen(url)
# -------------------------------------------------
# Line057 = End of script
)
Can such extra mqtt safely be fitted besides the 'normal' MQTT as set through 'Setup/Hardware' for handling of other dataflows, such as for ESP8266 and TTN?Installation
This requires installing the paho mqtt and influxdb python libraries.
On a debian(ish) system that can be done by:
# for python3# for python2Code: Select all
sudo apt-get install -y python3-pip && sudo pip3 install paho-mqtt influxdb
Code: Select all
sudo apt-get install -y python-pip && sudo pip install paho-mqtt influxdb
Code: Select all
#!/usr/bin/python3
# -*- coding = utf-8 to enable reading by simple editors -*-
# Reading UDP messages from Tempest_sensor and generating JSON-File & XML-File.
# Original script by p-doyle is at https://github.com/p-doyle/Simple-WeatherFlow-Python-Listener
# This adaptation only tested with python3.7. XML-generation requires install of dicttoxml for python3.
# --------------------------------------------------
# Line 006 = PREPARATION & SETTINGs
# --------------------------------------------------
# Imports for script-operation
import socket
import select
import time
import struct
import pprint
import json
import datetime
import dicttoxml
# create broadcast listener socket
def create_broadcast_listener_socket(broadcast_ip, broadcast_port):
b_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
b_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
b_sock.bind(('', broadcast_port))
mreq = struct.pack("4sl", socket.inet_aton(broadcast_ip), socket.INADDR_ANY)
b_sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
return b_sock
# map for the 'obs_st' type observations
OBS_ST_MAP = [
('Time Epoch', 'Seconds'),
# ('Wind Lull (minimum 3 second sample)', 'm/s'),
# ('Wind Avg (average over report interval)', 'm/s'),
# ('Wind Gust (maximum 3 second sample)', 'm/s'),
('Wind Lull', 'm/s'),
('Wind Avg', 'm/s'),
('Wind Gust', 'm/s'),
('Wind Direction', 'degrees'),
('Wind Sample Interval', 'seconds'),
('Station Pressure', 'mb'),
('Air Temperature', 'C'),
('Relative Humidity', '%'),
('Illuminance', 'Lux'),
('UV', 'Index'),
('Solar Radiation', 'W/m^2'),
('Precip Accumulated', 'mm'),
('Precipitation Type', '0 = none, 1 = rain, 2 = hail'),
# ('Lightning Strike Avg Distance', 'km'),
('Lightning Strike Distance', 'km'),
('Lightning Strike Count', ''),
('Battery', 'Volts'),
('Report Interval', 'Minutes')
]
# map for the 'rapid_wind' type observations
RAPID_WIND_MAP = [
('Time Epoch', 'Seconds'),
('Wind Speed', 'm/s'),
('Wind Direction', 'Degrees')
]
# map for the 'evt_strike' type observations
EVT_STRIKE_MAP = [
('Time Epoch', 'Seconds'),
('Distance', 'km'),
('Energy', '')
]
# ip/port to listen to
BROADCAST_IP = '239.255.255.250'
BROADCAST_PORT = 50222
# create the listener socket
sock_list = [create_broadcast_listener_socket(BROADCAST_IP, BROADCAST_PORT)]
# --------------------------------------------------
# Line 080 = SCRIPT EXECUTION
# --------------------------------------------------
while True:
# small sleep otherwise this will loop too fast between messages and eat a lot of CPU
time.sleep(0.01)
# wait until there is a message to read
readable, writable, exceptional = select.select(sock_list, [], sock_list, 0)
# for each socket with a message
for s in readable:
data, addr = s.recvfrom(4096)
# convert data to json
data_json = json.loads(data)
if data_json['type'] == 'obs_st':
# takes the map list and the list of observations and creates a dictionary with the first value in
# the map as the key and the first value in the list of observations as the value, second value
# to second value, etc
# Original scriptline for full map conversion to dictionary:
# observations = dict(zip(OBS_ST_MAP, data_json['obs'][0]))
# For edit of the OBS_ST_MAP variable to just be strings and get rid of the units:
string_map = [key[0] for key in OBS_ST_MAP]
observations = dict(zip(string_map , data_json['obs'][0]))
# If you want to keep the units you could concatenate them together with:
# string_map = [key + " - " + units for key, units in OBS_ST_MAP]
# Original scriptline for correct fill of datetime in original setup incl. units:
# observations['Datetime'] = datetime.datetime.fromtimestamp(observations[('Time Epoch', 'Seconds')])
# Insert dictionary into JSON-file
with open('/home/pi/Tempest_Sensor.json', 'w') as outfile:
json.dump(observations, outfile)
# Convert dictionary to XML-file & print
TempestObs_xml = dicttoxml.dicttoxml(observations, attr_type=False)
print(TempestObs_xml)
TempestObs_xml = str(TempestObs_xml)
xml_output = open("/home/pi/Tempest_Sensor.xml",'w')
xml_output.write(TempestObs_xml)
xml_output.close()
print ('List of Obs-values')
pprint.pprint(observations)
elif data_json['type'] == 'rapid_wind':
# Original scriptlines:
# observations = dict(zip(RAPID_WIND_MAP, data_json['ob']))
# observations['Datetime'] = datetime.datetime.fromtimestamp(observations[('Time Epoch', 'Seconds')])
# For edit of the RAPID_WIND_MAP variable to just be strings and get rid of the units:
string_map = [key[0] for key in RAPID_WIND_MAP]
observations = dict(zip(string_map , data_json['ob']))
# Insert dictionary into JSON-file
with open('/home/pi/Tempest_RWind.json', 'w') as outfile:
json.dump(observations, outfile)
print ('RapidWind')
pprint.pprint(observations)
elif data_json['type'] == 'evt_strike':
# Original scriptlines:
# observations = dict(zip(EVT_STRIKE_MAP, data_json['evt']))
# observations['Datetime'] = datetime.datetime.fromtimestamp(observations[('Time Epoch', 'Seconds')])
# For edit of the EVT_STRIKE_MAP variable to just be strings and get rid of the units:
string_map = [key[0] for key in EVT_STRIKE_MAP]
observations = dict(zip(string_map , data_json['evt'][0]))
# Insert dictionary into JSON-file
with open('/home/pi/Tempest_SInfo.json', 'w') as outfile:
json.dump(observations, outfile)
print ('StrikeInfo')
pprint.pprint(observations)
elif data_json['type'] == 'evt_precip':
# no other information other than the timestamp is included so just print a simple message
print ('StartOfRain')
print('It started raining at {}!'.format(datetime.datetime.fromtimestamp(data_json['evt'][0])))
# Insert StartOfRain into JSON-file
SORTime = format(datetime.datetime.fromtimestamp(data_json['evt'][0]))
with open('/home/pi/Tempest_SORain.json', 'w') as outfile:
json.dump(SORTime, outfile)
else:
print('OtherInfo')
print(data_json)
print
Code: Select all
b'<?xml version="1.0" encoding="UTF-8" ?><root><Time_Epoch>1618391739</Time_Epoch><Wind_Lull>0.0</Wind_Lull><Wind_Avg>0.0</Wind_Avg><Wind_Gust>0.0</Wind_Gust><Wind_Direction>0</Wind_Direction><Wind_Sample_Interval>3</Wind_Sample_Interval><Station_Pressure>1032.16</Station_Pressure><Air_Temperature>6.59</Air_Temperature><Relative_Humidity>64.78</Relative_Humidity><Illuminance>72422</Illuminance><UV>4.39</UV><Solar_Radiation>604</Solar_Radiation><Precip_Accumulated>0.0</Precip_Accumulated><Precipitation_Type>0</Precipitation_Type><Lightning_Strike_Distance>0</Lightning_Strike_Distance><Lightning_Strike_Count>0</Lightning_Strike_Count><Battery>2.714</Battery><Report_Interval>1</Report_Interval></root>'
Toulon7559 wrote: ↑Thursday 08 April 2021 17:44 An effectively working frontend is the start for reading of the UDP-messages.
_______________________________________
Many thanks for posting, very helpful!
I was wondering if the Tempest API supports a more rapid polling of the wind sensor data?
And if anyone has a working python implementation of this?
From what I've been able to gather, the Tempest internally pre-averages the wind samples and provides the average (of however many internal samples it is), to the UDP stream roughly every 3 seconds. Assuming this is correct, a high wind gust could easily be averaged to something much lower, and effectively missed.
For anyone who knows the inner workings of the Tempest API, is there a way to directly poll -just the wind speed value- on-demand at a rate of at least once per second ? (2 or 3 per second preferred)
Thanks in advance