Skip to content

Commit 22de954

Browse files
authored
Merge pull request #2243 from adafruit/azure-pi
Adding files for raspberry pi azure project
2 parents 20a187d + c5bd022 commit 22de954

File tree

8 files changed

+43505
-0
lines changed

8 files changed

+43505
-0
lines changed

Raspberry_Pi_Azure_IoT_Hub_Dashboard/azure_pi/OstrichSans-Heavy-60.bdf

Lines changed: 14727 additions & 0 deletions
Large diffs are not rendered by default.

Raspberry_Pi_Azure_IoT_Hub_Dashboard/azure_pi/OstrichSans-Heavy-88.bdf

Lines changed: 20577 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
#!/usr/bin/env python3
6+
7+
# pylint: disable=unused-import
8+
import json
9+
from subprocess import Popen, PIPE
10+
from datetime import datetime, timezone
11+
import requests
12+
import displayio
13+
import adafruit_imageload
14+
from adafruit_display_text import label
15+
from adafruit_bitmap_font import bitmap_font
16+
from adafruit_display_shapes.circle import Circle
17+
from azure.iot.hub import IoTHubRegistryManager
18+
from azure.iot.hub.models import Twin, TwinProperties, QuerySpecification, QueryResult
19+
from azure.eventhub import EventHubConsumerClient, TransportType
20+
from blinka_displayio_pygamedisplay import PyGameDisplay
21+
22+
# ***
23+
24+
# UPDATE THESE VARIABLES WITH YOUR IOT HUB INFORMATION
25+
26+
# event hub compatible end point from the built-in endpoints page
27+
event_connection = "YOUR-EVENT-HUB-CONNECTION-STRING-HERE"
28+
# Primary Connection String with registry read and service connect rights
29+
# managed on shared access polices page
30+
status_connection = "YOUR-STATUS-CONNECTION-STRING-HERE"
31+
32+
# iot hub subscription ID
33+
# found on the overview of your iot hub
34+
# format: ########-####-####-############
35+
subscription_id = "YOUR-SUBSCRIPTION-ID-HERE"
36+
37+
# device id's (device names in iot hub)
38+
qt_py = "YOUR-QT-PY-DEVICE-HERE"
39+
tft_feather = "YOUR-TFT-FEATHER-DEVICE-HERE"
40+
s3_feather = "YOUR-S3-FEATHER-DEVICE-HERE"
41+
42+
# ***
43+
44+
# array of devices
45+
devices = [qt_py, tft_feather, s3_feather]
46+
47+
# create display
48+
display = PyGameDisplay(width=1920, height=1080)
49+
50+
# open bitmap background with imageload
51+
bitmap, palette = adafruit_imageload.load(
52+
"/home/pi/azure_pi/piBG_0.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette
53+
)
54+
55+
background = displayio.TileGrid(bitmap, pixel_shader=palette)
56+
57+
# load font files
58+
font_file = "/home/pi/azure_pi/OstrichSans-Heavy-88.bdf"
59+
smallFont_file = "/home/pi/azure_pi/OstrichSans-Heavy-60.bdf"
60+
font = bitmap_font.load_font(font_file)
61+
small_font = bitmap_font.load_font(smallFont_file)
62+
63+
# text objects
64+
# co2 data
65+
co2_text = label.Label(font, text = "500", color = 0xFFFFFF, x = 611, y =800)
66+
67+
# living room data
68+
lr_temp = label.Label(font, text = "72°F", color = 0xFFFFFF, x = 90, y = 424)
69+
lr_humid = label.Label(font, text = "52%", color = 0xFFFFFF, x = 405, y = 424)
70+
lr_press = label.Label(font, text = "1005", color = 0xFFFFFF, x = 715, y = 424)
71+
72+
# bedroom data
73+
bd_temp = label.Label(font, text = "72°F", color = 0xFFFFFF, x = 1050, y =424)
74+
bd_humid = label.Label(font, text = "52%", color = 0xFFFFFF, x = 1365, y =424)
75+
bd_press = label.Label(font, text = "1005", color = 0xFFFFFF, x = 1675, y =424)
76+
77+
# cost, timestamp and last device check-in text
78+
timestamp_text = label.Label(small_font, text = "00:00AM 00/00/00",
79+
color = 0xFFFFFF, x = 1216, y =886)
80+
lastDevice_text = label.Label(small_font, text = "device",
81+
color = 0xFFFFFF, x = 1450, y =805)
82+
cost_text = label.Label(small_font, text = "$0.00",
83+
color = 0xFFFFFF, x = 1511, y =734)
84+
85+
# status circles, defaults to white
86+
qt_status = Circle(908, 695, 22, fill=0xFFFFFF)
87+
lr_status = Circle(908, 52, 22, fill=0xFFFFFF)
88+
bd_status = Circle(1869, 52, 22, fill=0xFFFFFF)
89+
90+
# array of status circles
91+
status_circles = [qt_status, lr_status, bd_status]
92+
93+
# array of display objects
94+
display_objects = [background, co2_text, lr_temp, lr_humid, lr_press,
95+
bd_temp, bd_humid, bd_press, timestamp_text, lastDevice_text,
96+
cost_text, qt_status, lr_status, bd_status]
97+
98+
# display group
99+
main_group = displayio.Group()
100+
101+
# add all display objects from array to the display group
102+
for x in display_objects:
103+
main_group.append(x)
104+
105+
display.show(main_group)
106+
107+
# convert UTC time to local timezone
108+
def utc_to_local(utc_dt):
109+
return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)
110+
111+
# function for an incoming event from one of the devices
112+
# pylint: disable=too-many-locals
113+
def on_event_batch(partition_context, events):
114+
# gets bearer token from azure cli in terminal
115+
cost_json_index = 0
116+
output = Popen(['az', 'account', 'get-access-token'], stdout=PIPE)
117+
# parses the output into a json and grabs the bearer token
118+
bear = json.loads(output.communicate()[0].decode('utf-8').strip())
119+
# creates headers to include with GET HTTP request for cost data
120+
headers = {'Authorization': 'Bearer ' + bear['accessToken'], 'Content-Type': 'application/json'}
121+
# updates request URL with your subscription ID
122+
# pylint: disable=line-too-long
123+
url = "https://management.azure.com/subscriptions/{}/providers/Microsoft.Consumption/usageDetails?api-version=2021-10-01".format(subscription_id)
124+
# makes HTTP request
125+
response = requests.get(url, headers=headers)
126+
# packs the HTTP response into a JSON
127+
feed = response.json()
128+
# grabs the cost per day from the JSON feed
129+
cost = feed['value'][cost_json_index]['properties']['costInBillingCurrency']
130+
# the JSON index for cost can move depending on the data recieved
131+
# adjusts index until a value is recieved
132+
while cost == 0:
133+
cost_json_index += 1
134+
cost = feed['value'][cost_json_index]['properties']['costInBillingCurrency']
135+
# iterates through incoming events
136+
for event in events:
137+
# gets timestamp for event
138+
clock = utc_to_local(event.enqueued_time)
139+
# calculates the month to date cost based on the date
140+
month_cost = cost*((float(clock.strftime("%d")) - 1))
141+
round(month_cost, 2)
142+
# updates cost text
143+
cost_text.text = "$%.2f" % month_cost
144+
# updates timestamp text
145+
timestamp_text.text = clock.strftime("%I:%M%p %m/%d/%y")
146+
# gets the incoming event as a JSON feed
147+
telemetry = event.body_as_json()
148+
# grabs the device ID from the JSON
149+
device = event.system_properties[b'iothub-connection-device-id']
150+
# converts the device ID to a string
151+
string_device = device.decode("utf-8")
152+
# updates last device text
153+
lastDevice_text.text = string_device
154+
# if the device is the qt_py
155+
if string_device == qt_py:
156+
# update co2 text
157+
co2_text.text = str(telemetry['CO2'])
158+
# if the device is the tft feather
159+
if string_device == tft_feather:
160+
# update living room sensor text
161+
lr_temp.text = "%s°F" % str(telemetry['Temperature'])
162+
lr_humid.text = "%s%%" % str(telemetry['Humidity'])
163+
lr_press.text = str(telemetry['Pressure'])
164+
# if the device is the s3 feather
165+
if string_device == s3_feather:
166+
# update bedroom sensor text
167+
bd_temp.text = "%s°F" % str(telemetry['Temperature'])
168+
bd_humid.text = "%s%%" % str(telemetry['Humidity'])
169+
bd_press.text = str(telemetry['Pressure'])
170+
# iterate through the status circles
171+
for d in range(0, 3):
172+
# get connection status of all 3 devices using device twin
173+
twin = status_client.get_twin(devices[d])
174+
# if a device is connected, make the status circle green
175+
if twin.connection_state == 'Connected':
176+
status_circles[d].fill = 0x00FF00
177+
# if a device is disconnected, make the status circle red
178+
if twin.connection_state == 'Disconnected':
179+
status_circles[d].fill = 0xFF0000
180+
# update the read partition from event hub
181+
partition_context.update_checkpoint()
182+
# in case of error with an incoming event
183+
def on_error(partition_context, error):
184+
if partition_context:
185+
print("An exception: {} occurred during receiving from Partition: {}.".format(
186+
partition_context.partition_id,
187+
error
188+
))
189+
else:
190+
print("An exception: {} occurred during the load balance process.".format(error))
191+
# connect to event hub
192+
client = EventHubConsumerClient.from_connection_string(
193+
conn_str = event_connection,
194+
consumer_group = "$default",
195+
)
196+
# connect to device twin
197+
status_client = IoTHubRegistryManager(status_connection)
198+
199+
# while the display is active
200+
while display.running:
201+
try:
202+
# recieve incoming events
203+
client.receive_batch(
204+
on_event_batch = on_event_batch,
205+
on_error = on_error
206+
)
207+
# keyboard exception
208+
except KeyboardInterrupt:
209+
print("Receiving has ")
1.84 MB
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)