Skip to content

Commit

Permalink
No longer using deveui from TTN devices for Ubidots device label, web…
Browse files Browse the repository at this point in the history
…app map now colour codes icons based upon time last seen.
  • Loading branch information
dajtxx committed Sep 11, 2024
1 parent 35c1c38 commit 69ad24e
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 28 deletions.
10 changes: 2 additions & 8 deletions src/python/delivery/UbidotsWriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,21 +107,15 @@ def on_message(self, pd: PhysicalDevice, ld: LogicalDevice, msg: dict[Any], retr
new_device = True
ld.properties['ubidots'] = {}
# TODO: Remove the device source specific code here and always use a random
# UUID for the Ubidots label. This cannot be done until the current TTN ubifunction
# is switched off because the broker must be able to determine the same device label
# used by the ubifunction when it creates Ubidots devices.
if pd.source_name == BrokerConstants.TTN:
ld.properties['ubidots']['label'] = pd.source_ids['dev_eui']
lu.cid_logger.info(f'Using physical device eui for label: {ld.properties["ubidots"]["label"]}', extra=msg)
elif pd.source_name == BrokerConstants.GREENBRAIN:
# UUID for the Ubidots label.
if pd.source_name == BrokerConstants.GREENBRAIN:
lu.cid_logger.info('Using system-station-sensor-group ids as label', extra=msg)
system_id = pd.source_ids['system_id']
station_id = pd.source_ids['station_id']
sensor_group_id = pd.source_ids['sensor_group_id']
ubi_label = f'{system_id}-{station_id}-{sensor_group_id}'
ld.properties['ubidots']['label'] = ubi_label
else:
lu.cid_logger.info('Using a UUID as Ubidots label', extra=msg)
ld.properties['ubidots']['label'] = uuid.uuid4()

#
Expand Down
4 changes: 0 additions & 4 deletions src/www/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
# syntax=docker/dockerfile:1

FROM python:3.10

WORKDIR /app

COPY src/www/requirements.txt requirements.txt
RUN pip install -r requirements.txt

COPY . .

EXPOSE 5000

CMD [ "python", "./app/main.py"]
104 changes: 88 additions & 16 deletions src/www/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
import logging
import pandas as pd
import time
from typing import Tuple
from typing import Dict, Tuple
import uuid
from zoneinfo import ZoneInfo

from flask import Flask, render_template, request, redirect, url_for, session, send_from_directory, send_file

import folium
import folium.plugins

import paho.mqtt.client as mqtt
import os
from datetime import timedelta, datetime, timezone
Expand Down Expand Up @@ -78,22 +80,36 @@ def parse_location(loc_str: str) -> Tuple[bool, Location | None]:
return True, location


def time_since(date: datetime) -> str:
_warning_seconds = 3600 * 6 ### How many seconds ago a device was seen to show a warning.
_error_seconds = 3600 * 12 ### How many seconds ago a device was seen to show an error.
_seconds_per_day = 3600 * 24

def time_since(date: datetime) -> Dict[str, int|str]:
now = datetime.now(timezone.utc)
date_utc = date.astimezone(timezone.utc)
delta = now - date_utc
days = delta.days
hours = int(delta.seconds / 3600)
minutes = int((delta.seconds % 3600) / 60)
seconds = int(delta.seconds % 60)

ret_val = {
'days': days,
'hours': hours,
'minutes': minutes,
'delta_seconds': delta.seconds + (delta.days * _seconds_per_day)
}

if days > 0:
return f'{days} days ago'
ret_val['desc'] = f'{days} days ago'
elif hours > 0:
return f'{hours} hours ago'
ret_val['desc'] = f'{hours} hours ago'
elif minutes > 0:
return f'{minutes} minutes ago'
ret_val['desc'] = f'{minutes} minutes ago'
else:
ret_val['desc'] = f'{seconds} seconds ago'

return f'{seconds} seconds ago'
return ret_val


#-------------
Expand Down Expand Up @@ -474,17 +490,46 @@ def logical_device_form(uid):
@app.route('/map', methods=['GET'])
def show_map():
try:
center_map = folium.Map(location=[-32.2400951991083, 148.6324743348766], title='PhysicalDeviceMap', zoom_start=10)
# folium.Marker([-31.956194913619864, 115.85911692112582], popup="<i>Mt. Hood Meadows</i>", tooltip='click me').add_to(center_map)
# Map limits cover NSW.
center_map = folium.Map(
location=[-32.42, 147.5],
min_lat=-36.8, max_lat=-29.6, min_lon=141.7, max_lon=152.9,
max_bounds=True,
title='IoTa Logical Devices',
zoom_start=7)

live_nodes = folium.FeatureGroup(name='Live', show=False)
late_nodes = folium.FeatureGroup(name='Late')
dead_nodes = folium.FeatureGroup(name='Missing')

live_markers = []
late_markers = []
dead_markers = []

data: List[LogicalDevice] = get_logical_devices(session.get('token'), include_properties=True)
for dev in data:
if dev.location is not None and dev.location.lat is not None and dev.location.long is not None:
color = 'blue'
color = 'green'
icon_name = 'circle'
marker_list = live_markers

last_seen = None
if dev.last_seen is None:
last_seen_desc = 'Never'
icon_name = 'circle-question'
color = 'red'
marker_list = dead_markers
else:
last_seen_desc = time_since(dev.last_seen)
last_seen = time_since(dev.last_seen)
last_seen_desc = last_seen['desc']
if last_seen['delta_seconds'] > _error_seconds:
color = 'red'
icon_name = 'circle-xmark'
marker_list = dead_markers
elif last_seen['delta_seconds'] > _warning_seconds:
color = 'orange'
icon_name = 'circle-exclamation'
marker_list = late_markers

popup_str = f'<span style="white-space: nowrap;">Device: {dev.uid} / {dev.name}<br>Last seen: {last_seen_desc}'

Expand All @@ -497,14 +542,41 @@ def show_map():

popup_str = popup_str + '</span>'

folium.Marker([dev.location.lat, dev.location.long],
marker = folium.Marker([dev.location.lat, dev.location.long],
popup=popup_str,
icon=folium.Icon(color=color, icon='cloud'),
tooltip=dev.name).add_to(center_map)
icon=folium.Icon(color=color, icon=icon_name, prefix='fa'),
tooltip=f'{dev.name}, last seen {last_seen_desc}')

marker_list.append(marker)

# This was an attempt to set the draw order of the markers. It did not work
# but the code has been kept in case having this structure is useful or a
# way to make it work is found.
for marker in live_markers:
live_nodes.add_child(marker)

for marker in late_markers:
late_nodes.add_child(marker)

for marker in dead_markers:
dead_nodes.add_child(marker)

center_map.add_child(live_nodes)
center_map.add_child(late_nodes)
center_map.add_child(dead_nodes)

# It seems to be important to add the LayerControl down here. Doing it before
# the FeatureGroups are defined doesn't work.
folium.LayerControl(collapsed=False).add_to(center_map)
folium.plugins.Fullscreen(
position="topleft",
title="Full screen",
title_cancel="Exit full screen",
force_separate_button=True,
).add_to(center_map)

return center_map.get_root().render()

return center_map._repr_html_()
# center_map
# return render_template('map.html')
except requests.exceptions.HTTPError as e:
return render_template('error_page.html', reason=e), e.response.status_code

Expand Down
Binary file modified src/www/requirements.txt
Binary file not shown.

0 comments on commit 69ad24e

Please sign in to comment.