diff --git a/client/ios/Widget/LocationSelection.intentdefinition b/client/ios/Widget/LocationSelection.intentdefinition
index 748e916..0aac055 100644
--- a/client/ios/Widget/LocationSelection.intentdefinition
+++ b/client/ios/Widget/LocationSelection.intentdefinition
@@ -9,11 +9,11 @@
INIntentDefinitionNamespace
88xZPY
INIntentDefinitionSystemVersion
- 19H2
+ 20G95
INIntentDefinitionToolsBuildVersion
- 12A7300
+ 12E507
INIntentDefinitionToolsVersion
- 12.0.1
+ 12.5.1
INIntents
@@ -30,10 +30,10 @@
INIntentIneligibleForSuggestions
INIntentLastParameterTag
- 4
+ 6
INIntentManagedParameterCombinations
- location
+ location,display_purpleair
INIntentParameterCombinationSupportsBackgroundExecution
@@ -81,6 +81,48 @@
INIntentParameterType
Placemark
+
+ INIntentParameterConfigurable
+
+ INIntentParameterDisplayName
+ Show Raw PurpleAir values
+ INIntentParameterDisplayNameID
+ gyzQoM
+ INIntentParameterDisplayPriority
+ 2
+ INIntentParameterMetadata
+
+ INIntentParameterMetadataFalseDisplayName
+ false
+ INIntentParameterMetadataFalseDisplayNameID
+ C9JREb
+ INIntentParameterMetadataTrueDisplayName
+ true
+ INIntentParameterMetadataTrueDisplayNameID
+ Pp5b6V
+
+ INIntentParameterName
+ display_purpleair
+ INIntentParameterPromptDialogs
+
+
+ INIntentParameterPromptDialogCustom
+
+ INIntentParameterPromptDialogType
+ Configuration
+
+
+ INIntentParameterPromptDialogCustom
+
+ INIntentParameterPromptDialogType
+ Primary
+
+
+ INIntentParameterTag
+ 6
+ INIntentParameterType
+ Boolean
+
INIntentResponse
diff --git a/client/ios/Widget/Widget.swift b/client/ios/Widget/Widget.swift
index 6d902e0..ded5bb5 100644
--- a/client/ios/Widget/Widget.swift
+++ b/client/ios/Widget/Widget.swift
@@ -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
@@ -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")!))
}
}
@@ -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
@@ -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 {
diff --git a/client/swift/AQI/Download.swift b/client/swift/AQI/Download.swift
index d604fe5..2cdfac8 100644
--- a/client/swift/AQI/Download.swift
+++ b/client/swift/AQI/Download.swift
@@ -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 {
@@ -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?, Error?) -> Void) {
+public func downloadCompactReadings(type: ReadingType, onResponse: @escaping (GKRTree?, 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, *)
diff --git a/server/template.yaml b/server/template.yaml
index da86429..7d5f195 100644
--- a/server/template.yaml
+++ b/server/template.yaml
@@ -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
@@ -127,3 +128,6 @@ Parameters:
S3KeyCompact:
Type: String
Default: sensors.compact.pb
+ S3KeyRawCompact:
+ Type: String
+ Default: sensors.raw.compact.pb
diff --git a/server/update_data/app.py b/server/update_data/app.py
index 0c1746e..38b46d7 100644
--- a/server/update_data/app.py
+++ b/server/update_data/app.py
@@ -17,10 +17,11 @@ 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()
@@ -28,11 +29,12 @@ def update_sensor_data(
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, "
@@ -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"])