Skip to content

Commit

Permalink
MMM-DarkSkyForecast support with location picker
Browse files Browse the repository at this point in the history
- Add url_launcher and google_map_location_picker packages
- Add ModuleDarkSkyForecast with Google maps location picker
- Change module position picker header from "Position" to "Module
  position" to not confuse it with weather data location

Important notes

- Must generate a Google maps API key and add to AndroidManifest.xml and
  ModuleDarkSkyForecast.dart (look for PUT_GOOGLE_MAPS_API_KEY_HERE)
  This should be changed to a shared API key for the app. For now, see:
  https://pub.dev/packages/google_map_location_picker
- The MMM-DarkSkyForecast module must be based on a fork that uses
  OpenWeatherMap instead of DarkSky, since the latter is no longer free.
  Refer to https://github.com/smartmirrorinc/MMM-DarkSkyForecast
  • Loading branch information
tausen committed Nov 22, 2020
1 parent b039207 commit ea6dc7f
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 2 deletions.
5 changes: 5 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.smartmirror">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-feature android:name="android.hardware.location.network" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
Expand Down Expand Up @@ -39,6 +42,8 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Google Maps API key -->
<meta-data android:name="com.google.android.geo.API_KEY" android:value="PUT_GOOGLE_MAPS_API_KEY_HERE"/>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
Expand Down
1 change: 1 addition & 0 deletions lib/modules/Module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Module moduleFromString(Map<String, dynamic> json) {
"updatenotification": ModuleUpdateNotification.instantiate,
"currentweather": ModuleWeather.instantiate,
"weatherforecast": ModuleForecast.instantiate,
"MMM-DarkSkyForecast": ModuleDarkSkyForecast.instantiate,
};

if (!modules.keys.contains(json["module"])) {
Expand Down
2 changes: 1 addition & 1 deletion lib/modules/PositionedModule.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class PositionedModule extends Module {
Widget icon = Icon(Icons.picture_in_picture);
ListTile tile = ListTile(
leading: icon,
title: Text("Position", style: TextStyle(fontWeight: FontWeight.bold)),
title: Text("Module position", style: TextStyle(fontWeight: FontWeight.bold)),
subtitle: dropdown);
widgets.add(
Card(child: Column(mainAxisSize: MainAxisSize.max, children: [tile])));
Expand Down
174 changes: 174 additions & 0 deletions lib/modules/components/ModuleDarkSkyForecast.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
part of components;

class ModuleDarkSkyForecast extends PositionedModule {
double latitude, longitude;
int hourlyForecastInterval;
String apikey;
GoogleMapController mapController;

ModuleDarkSkyForecast(id, module, position, _latitude, _longitude,
_hourlyForecastInterval, _apikey)
: latitude = _latitude,
longitude = _longitude,
hourlyForecastInterval = _hourlyForecastInterval,
apikey = _apikey,
super(id, module, position);

factory ModuleDarkSkyForecast.fromJson(Map<String, dynamic> json) {
double latitude = 57.048820;
double longitude = 9.921747;
int hourlyForecastInterval = 6;
String apikey = "";

if (json.containsKey("config")) {
if (json["config"].containsKey("latitude")) {
latitude = json['config']['latitude'];
}
if (json["config"].containsKey("longitude")) {
longitude = json['config']['longitude'];
}
if (json["config"].containsKey("hourlyForecastInterval")) {
hourlyForecastInterval = json['config']['hourlyForecastInterval'];
}
if (json["config"].containsKey("apikey")) {
apikey = json['config']['apikey'];
}
}

return ModuleDarkSkyForecast(
json['_meta']['id'],
json['module'],
modulePositionFromString(json['position']),
latitude,
longitude,
hourlyForecastInterval,
apikey);
}

static instantiate(Map<String, dynamic> json) =>
ModuleDarkSkyForecast.fromJson(json);

@override
String toString() {
return "{id:$id, module:$module, position:${position.toString()}, " +
"latitude: $latitude, " +
"longitude: $longitude, " +
"hourlyForecastInterval: $hourlyForecastInterval, " +
"apikey: $apikey}";
}

void _onMapCreated(GoogleMapController controller) {
mapController = controller;
}

// TODO: this does not belong here
String capString(String x) {
if (x.length < 13) {
return x;
} else {
return x.substring(0, 10) + "...";
}
}

@override
void buildWidgets(BuildContext context, Function refresh) {
super.buildWidgets(context, refresh);

// API key input field
widgets.add(Card(
child: Column(children: [
ListTile(
leading: Icon(Icons.vpn_key),
title: TextField(
onChanged: (String value) {
apikey = value;
},
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: "API key",
labelStyle: TextStyle(fontWeight: FontWeight.bold),
helperText: "Old value: " + capString(apikey)),
)),
ListTile(
leading: Icon(Icons.help),
title: Text("Where do I find my API key?"),
onTap: () async {
await launch("https://home.openweathermap.org/api_keys");
},
)
])));

// Location picker
widgets.add(Card(
child: Column(
children: [
// Header
ListTile(
leading: Icon(Icons.my_location),
title: Text("Location",
style: TextStyle(fontWeight: FontWeight.bold))),

// Map showing current position (zoom only, no scrolling)
SizedBox(
child: GoogleMap(
onMapCreated: _onMapCreated,
mapToolbarEnabled: true,
liteModeEnabled: false,
scrollGesturesEnabled: false,
initialCameraPosition: CameraPosition(
target: LatLng(latitude, longitude), zoom: 10),
markers: Set<Marker>.of([
Marker(
markerId: MarkerId("Current"),
position: LatLng(latitude, longitude))
]),
),
height: 200),

// Button to open location picker
RaisedButton(
onPressed: () async {
LocationResult result = await showLocationPicker(context,
"PUT_GOOGLE_MAPS_API_KEY_HERE", // TODO: app-specific key
initialCenter: LatLng(56.224288, 11.195565), // ~mid Denmark
automaticallyAnimateToCurrentLocation: false,
myLocationButtonEnabled: true,
layersButtonEnabled: false,
// accuracy must be 'best' or getting user position does not
// work, might be related to
// https://github.com/Baseflow/flutter-geolocator/issues/117 ?
desiredAccuracy: LocationAccuracy.best,
initialZoom: 6);

latitude = result.latLng.latitude;
longitude = result.latLng.longitude;

// move "current position" map to new coords
mapController.moveCamera(
CameraUpdate.newLatLng(LatLng(latitude, longitude)),
);
refresh(this);
},

child: Text('Pick location'),
),
],
),
));
}

@override
Map<String, dynamic> toJson() {
Map<String, dynamic> json = super.toJson();
json['config'] = {
'apikey': apikey,
'forecastLayout': "table",
'hourlyForecastInterval': hourlyForecastInterval,
'label_timeFormat': "HH:mm",
"latitude": latitude,
"longitude": longitude
};
return json;
}
}
5 changes: 5 additions & 0 deletions lib/modules/components/components.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import 'package:smartmirror/modules/Module.dart';
import 'package:flutter/material.dart';
import 'package:smartmirror/modules/PositionedModule.dart';

import 'package:google_map_location_picker/google_map_location_picker.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:url_launcher/url_launcher.dart';

part "ModuleAlert.dart";
part "ModuleAnonymous.dart";
part "ModuleCalendar.dart";
Expand All @@ -15,3 +19,4 @@ part "ModuleNewsfeed.dart";
part "ModulePirSensor.dart";
part "ModuleUpdateNotification.dart";
part "ModuleWeather.dart";
part "ModuleDarkSkyForecast.dart";
3 changes: 2 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ dependencies:
sdk: flutter
http: ^0.12.2
multicast_dns: ^0.2.2

google_map_location_picker: ^4.1.2+1
url_launcher: ^5.7.10

# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
Expand Down

0 comments on commit ea6dc7f

Please sign in to comment.