Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 47 additions & 5 deletions client/ios/Widget/LocationSelection.intentdefinition
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
<key>INIntentDefinitionNamespace</key>
<string>88xZPY</string>
<key>INIntentDefinitionSystemVersion</key>
<string>19H2</string>
<string>20G95</string>
<key>INIntentDefinitionToolsBuildVersion</key>
<string>12A7300</string>
<string>12E507</string>
<key>INIntentDefinitionToolsVersion</key>
<string>12.0.1</string>
<string>12.5.1</string>
<key>INIntents</key>
<array>
<dict>
Expand All @@ -30,10 +30,10 @@
<key>INIntentIneligibleForSuggestions</key>
<true/>
<key>INIntentLastParameterTag</key>
<integer>4</integer>
<integer>6</integer>
<key>INIntentManagedParameterCombinations</key>
<dict>
<key>location</key>
<key>location,display_purpleair</key>
<dict>
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
<true/>
Expand Down Expand Up @@ -81,6 +81,48 @@
<key>INIntentParameterType</key>
<string>Placemark</string>
</dict>
<dict>
<key>INIntentParameterConfigurable</key>
<true/>
<key>INIntentParameterDisplayName</key>
<string>Show Raw PurpleAir values</string>
<key>INIntentParameterDisplayNameID</key>
<string>gyzQoM</string>
<key>INIntentParameterDisplayPriority</key>
<integer>2</integer>
<key>INIntentParameterMetadata</key>
<dict>
<key>INIntentParameterMetadataFalseDisplayName</key>
<string>false</string>
<key>INIntentParameterMetadataFalseDisplayNameID</key>
<string>C9JREb</string>
<key>INIntentParameterMetadataTrueDisplayName</key>
<string>true</string>
<key>INIntentParameterMetadataTrueDisplayNameID</key>
<string>Pp5b6V</string>
</dict>
<key>INIntentParameterName</key>
<string>display_purpleair</string>
<key>INIntentParameterPromptDialogs</key>
<array>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogType</key>
<string>Configuration</string>
</dict>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogType</key>
<string>Primary</string>
</dict>
</array>
<key>INIntentParameterTag</key>
<integer>6</integer>
<key>INIntentParameterType</key>
<string>Boolean</string>
</dict>
</array>
<key>INIntentResponse</key>
<dict>
Expand Down
12 changes: 9 additions & 3 deletions client/ios/Widget/Widget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct MapProvider : IntentTimelineProvider {

func getSnapshot(for intent: LocationSelectionIntent, in context: Context, completion: @escaping (MapEntry) -> Void) {
var location = self._location(for: intent)
let readingType = self._readingType(for: intent)
if location == nil {
let locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
Expand All @@ -48,7 +49,7 @@ struct MapProvider : IntentTimelineProvider {
}
}
}
self._mapImage(location: location, size: context.displaySize) { (mapImage) in
self._mapImage(location: location, size: context.displaySize, readingType: readingType) { (mapImage) in
completion(MapEntry(date: Date(), mapImage: mapImage ?? UIImage(named: "MapPlaceholder")!))
}
}
Expand All @@ -65,7 +66,12 @@ struct MapProvider : IntentTimelineProvider {
return intent.location?.location
}

private func _mapImage(location: CLLocation?, size: CGSize, callback: @escaping (UIImage?) -> Void) {
private func _readingType(for intent: LocationSelectionIntent) -> AQI.ReadingType {
let displayPurpleAir = intent.display_purpleair?.boolValue ?? false
return displayPurpleAir ? .rawPurpleAir : .epaCorrected
}

private func _mapImage(location: CLLocation?, size: CGSize, readingType: AQI.ReadingType, callback: @escaping (UIImage?) -> Void) {
let coordinate: CLLocationCoordinate2D
if let location = location {
coordinate = location.coordinate
Expand All @@ -81,7 +87,7 @@ struct MapProvider : IntentTimelineProvider {
callback(nil)
return
}
AQI.downloadCompactReadings { (readings, error) in
AQI.downloadCompactReadings(type: readingType) { (readings, error) in
if let readings = readings {
var region = options.region
if size.width > size.height {
Expand Down
5 changes: 3 additions & 2 deletions client/swift/AQI/Download.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import GameKit
private let aqiDataURL = URL(string: "https://dfddnmlutocpt.cloudfront.net/sensors.pb")!
private let particulateMatterDataURL = URL(string: "https://dfddnmlutocpt.cloudfront.net/sensors.raw.pb")!
private let compactDataURL = URL(string: "https://dfddnmlutocpt.cloudfront.net/sensors.compact.pb")!
private let compactParticulateMatterDataURL = URL(string: "https://dfddnmlutocpt.cloudfront.net/sensors.raw.compact.pb")!

/// The type of air quality measurement
public enum ReadingType {
Expand Down Expand Up @@ -37,11 +38,11 @@ public func downloadReadings(type: ReadingType, onProgess: @escaping (Float) ->
/// - onResponse: The callback to which we report the parsed download response
@available(iOS 13.0, *)
@available(macOS 10.12, *)
public func downloadCompactReadings(onResponse: @escaping (GKRTree<Reading>?, Error?) -> Void) {
public func downloadCompactReadings(type: ReadingType, onResponse: @escaping (GKRTree<Reading>?, Error?) -> Void) {
let client = DownloadClient(onProgess: { (percentage) in
}, onResponse: onResponse)
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: client, delegateQueue: OperationQueue.main)
session.downloadTask(with: compactDataURL).resume()
session.downloadTask(with: type == .epaCorrected ? compactDataURL : compactParticulateMatterDataURL).resume()
}

@available(iOS 13.0, *)
Expand Down
4 changes: 4 additions & 0 deletions server/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Resources:
AWS_S3_OBJECT: !Ref S3Key
AWS_S3_OBJECT_RAW: !Ref S3KeyRaw
AWS_S3_OBJECT_COMPACT: !Ref S3KeyCompact
AWS_S3_OBJECT_RAW_COMPACT: !Ref S3KeyRawCompact
Policies:
- Statement:
- Sid: UpdateDataPolicy
Expand Down Expand Up @@ -127,3 +128,6 @@ Parameters:
S3KeyCompact:
Type: String
Default: sensors.compact.pb
S3KeyRawCompact:
Type: String
Default: sensors.raw.compact.pb
9 changes: 6 additions & 3 deletions server/update_data/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,24 @@ def update_sensor_data(
We download JSON from PurpleAir and convert to our proprietary Protocol
Buffer format, which is used by all clients.

We upload three versions of the protocol buffer data:
We upload four versions of the protocol buffer data:
(1) The corrected EPA AQI readings
(2) Raw PurpleAir PM2.5 AQI readings
(3) A compact data, with just a single AQI reading, used by the widget
(4) A raw compact data, to represent raw single AQI readings in the widget
"""
start_time = time.time()
raw = urllib.request.urlopen(purpleair.api_url(purpleair_api_key)).read()
download_time = time.time()
data = purpleair.parse_api(raw, epa_correction=True)
raw_data = purpleair.parse_api(raw, epa_correction=False)
compact_data = purpleair.compact_sensor_data(data)
raw_compact_data = purpleair.compact_sensor_data(raw_data)
parse_time = time.time()
s3 = boto3.client("s3")
update(s3, s3_bucket, data=data, object_name=s3_object)
update(s3, s3_bucket, data=compact_data, object_name=compact_s3_object)
update(s3, s3_bucket, data=raw_data, object_name=raw_s3_object)
update(s3, s3_bucket, data=raw_data, object_name=raw_compact_s3_object)
s3_time = time.time()
total_time = time.time() - start_time
logging.info("Processed PurpleAir in %.1fs (download: %.1fs, parse: %.1fs, "
Expand All @@ -55,4 +57,5 @@ def lambda_handler(event, context):
s3_bucket=os.environ["AWS_S3_BUCKET"],
s3_object=os.environ["AWS_S3_OBJECT"],
raw_s3_object=os.environ["AWS_S3_OBJECT_RAW"],
compact_s3_object=os.environ["AWS_S3_OBJECT_COMPACT"])
compact_s3_object=os.environ["AWS_S3_OBJECT_COMPACT"],
raw_compact_s3_object=os.environ["AWS_S3_OBJECT_RAW_COMPACT"])