Skip to content

Commit

Permalink
Development (#6)
Browse files Browse the repository at this point in the history
* Change REST api to new api with apiKey

* Add info of v2.0.0 to README

* Delete port setting
  • Loading branch information
Klettner committed Dec 9, 2020
1 parent 74f3525 commit a222255
Show file tree
Hide file tree
Showing 12 changed files with 257 additions and 137 deletions.
61 changes: 44 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![GitHub license](https://img.shields.io/github/license/Klettner/MM-Remote)](https://github.com/Klettner/MM-Remote/blob/master/LICENSE)
![GitHub All Releases](https://img.shields.io/github/downloads/Klettner/MM-Remote/total)

MM-Remote is an Android app to control your **MagicMirror** remotly via smartphone :iphone:.
MM-Remote is an Android app to control your [**MagicMirror**](https://magicmirror.builders/) remotely via a smartphone :iphone:.

![](assets/currentDeviceHomeTab.png)

Expand All @@ -21,44 +21,71 @@ MM-Remote is an Android app to control your **MagicMirror** remotly via smartpho
* Start a timer on the mirror
* Start a stop-watch :hourglass_flowing_sand: on the mirror
* Hide and reorder default commands on the Home-tab

### v2.0.0 ###
* Changed to new API of MMM-Remote-Control for the communication with the mirror, as the previously used one will be deprecated soon
* Increased security through the usage of apiKeys
* Removed port setting, because it was redundant (should always be port 8080)

 
## Dependencies ##

### Required :warning: (the app won't work without it): ###
| Module | Usage |
| ------ |------ |
| [MMM-Remote-Control](https://github.com/Jopyth/MMM-Remote-Control) | Communication between app and mirror |
| [MMM-Remote-Control](https://github.com/Jopyth/MMM-Remote-Control) (version 2.2.0 or higher)| Communication between app and mirror |

### Optional (without these, some default commands won't work): ###
If you do not use all the optional dependencies some of the default commands won't work, but such commands can be hidden in the settings.
| Module | Usage |
| ------ |------ |
| [MMM-BackgroundSlideshow](https://github.com/darickc/MMM-BackgroundSlideshow) | Controlling a photo-slideshow on the mirror |
| [MMM-Pages](https://github.com/edward-shen/MMM-pages) | Switching between different UI-pages |
| [MMM-StopwatchTimer](https://github.com/klettner/MMM-StopwatchTimer) | Controlling a timer/stop-watch on the mirror |
If you do not use all the optional dependencies some default commands won't work, but such commands can be hidden in the settings.

| Module | Usage |
| ------ |------ |
| [MMM-BackgroundSlideshow](https://github.com/darickc/MMM-BackgroundSlideshow) | Controlling a photo-slideshow on the mirror |
| [MMM-Pages](https://github.com/edward-shen/MMM-pages) | Switching between different UI-pages |
| [MMM-StopwatchTimer](https://github.com/klettner/MMM-StopwatchTimer) | Controlling a timer/stop-watch on the mirror |

 
## Set-up ##
### Option 1 (easy) ###
Click [here](https://github.com/Klettner/MM-Remote/releases) and choose the latest release. Pick the **app-release.apk** file and download [:arrow_down:](https://github.com/Klettner/MM-Remote/releases) it. This file should be compatible with every android phone. Once the file is downloaded to your phone, click it to install the app. A warning will popup as this app was not downloaded from the app store. If you ignore this warning, the app will install and your done.
Click [here](https://github.com/Klettner/MM-Remote/releases) and choose the latest release. Pick the **app-release.apk** file and download [:arrow_down:](https://github.com/Klettner/MM-Remote/releases) it. This file should be compatible with every android phone. Once the file is downloaded to your phone, click it to install the app. A warning will popup as this app was not downloaded from the app store. If you ignore this warning, the app will install, and you're done.
Once the app is installed you can delete the .apk file, it is not needed anymore.

### Option 2 (compile the app by yourselfe) ###
First you need to clone this repository with Git. The app is written in **Dart** with the help of googles **Flutter** framework. To compile the app you need to setup Flutter if you haven't done so already. If you have never used Flutter before, there is a good [installation guide and documentation](https://flutter.dev/docs/get-started/install) available. After Flutter is set-up, run ```flutter build apk``` or ```flutter build apk --split-per-abi``` in your terminal to create the apk files.
### Option 2 (compile the app by yourself) ###
First you need to clone this repository with Git. The app is written in **Dart** with the help of googles **Flutter** framework. To compile the app you need to set up Flutter if you haven't done so already. If you have never used Flutter before, there is a good [installation guide and documentation](https://flutter.dev/docs/get-started/install) available. After Flutter is set up, run ```flutter build apk``` or ```flutter build apk --split-per-abi``` in your terminal to create the apk files.

## Getting started ##
After starting the MM-Remote app, tab on the :heavy_plus_sign: on the bottom-right to add you MagicMirror.
- Give your mirror a name.
- Add it's IP-address to the next field (e.g. something like 192.168.0.0). You can get the IP-address by typing `hostname -I` in the console of the raspberry pi.
- Put the port in the last field (e.g. 8080) and click on create.
- Give your mirror a name
- Add it's IP-address to the next field (e.g. something like 192.168.0.0). You can get the IP-address by typing `hostname -I` in the console of the raspberry pi
- The last field requires the apiKey you have specified for the MMM-Remote-Control module in the config.js of your mirror (make sure it is correct, otherwise the app will not be able to communicate with the mirror)

**How to get your apiKey:**
Open the config.js file and search for MMM-Remote-Control. It should look something like this:
```
{
module: 'MMM-Remote-Control'
config: {
apiKey: 'bc2e979db92f4741afad01d5d18eb8e2'
}
},
```
If you can't find the attribute 'apiKey', add it to your config. You can choose the value of this attribute by yourself.
Don't make it to simple, think about it as a password for your mirror. This is the value you need to add in the apiKey
field when creating a device in the MM-Remote app. You can find more information about the apiKey [here](https://github.com/Jopyth/MMM-Remote-Control/blob/master/API/README.md).

Now you should be able to remote control your mirror. If you want to reorder or hide some of the default commands displayed in the **HOME** tab, go to settings and check :white_check_mark: the default command boxes in the order in which these commands should be displayed. Keep in mind, if you don't use all the modules mentioned under dependencies, some of the buttons might not work (e.g. changing the displayed UI-pages of your mirror via the left and right arrows on the bottom only works if you are using the MMM-Pages module). You can always create you own commands in the **CUSTOM-COMMANDS** tab to extend the functionalities of the app. If there are any MagicMirror modules for which it is impractical to create your own **CUSTOM-COMMANDS** (e.g. because you need text input fileds, a slider or many buttons) let me know, I might consider creating :wrench: a default command for it in future releases.
Now you should be able to remote control your mirror. If you want to reorder or hide some default commands displayed in
the **HOME** tab, go to settings and check :white_check_mark: the default command boxes in the order in which these commands
should be displayed. Keep in mind, if you don't use all the modules mentioned under dependencies, some buttons might not
work (e.g. changing the displayed UI-pages of your mirror via the left and right arrows on the bottom only works if you
are using the MMM-Pages module). You can always create you own commands in the **CUSTOM-COMMANDS** tab to extend the
functionalities of the app. If there are any MagicMirror modules for which it is impractical to create your own **CUSTOM-COMMANDS** (e.g. because you need text input fields, a slider or many buttons) let me know, I might consider creating :wrench: a default command for it in future releases.

 
## :bulb: Trouble shooting :bulb: ##
- If you have performed the above steps but the mirror still does not respond, have a look at you *config.js* file. Usually at the beginning of the file there is something called the `ipWhitelist:`. Add the IP-address of your smartphone here to allow it sending commands to your mirror. If you don't know how to find out the IP-address of your smartphone a quick search with your favorite search engine will help.
- Check if the port you have specified in your *config.js* file is the same as in the app.
- If you have performed the above steps, but the mirror still does not respond, have a look at you *config.js* file. Usually at the beginning of the file there is something called the `ipWhitelist:`. Add the IP-address of your smartphone here to allow it sending commands to your mirror. If you don't know how to find out the IP-address of your smartphone a quick search with your favorite search engine will help.
- Check if you are using a recent version (2.2.0 or higher) of [MMM-Remote-Control](https://github.com/Jopyth/MMM-Remote-Control)
- Check if you have put in the apiKey which is specified in your config.js. To do this, open the settings of MM-Remote. Here you can see the currently active apiKey and can change it (e.g. if there is a typo present).
- If you have already installed an older version of the app previously, you might need to deinstall it before installing the new version
- If a command is not working even though you are using all the required dependencies (e.g. toggling the monitor on/off might not work if you are using a TV instead of a pc-monitor) you can overwrite the shell-command which is used with a command that is working for you. To do this, open your **config.js** file and add the **customCommand** section to the module [MMM-Remote-Control](https://github.com/Jopyth/MMM-Remote-Control).
```
module: 'MMM-Remote-Control',
Expand Down
6 changes: 3 additions & 3 deletions lib/models/deviceArguments.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
class DeviceArguments {
String deviceName;
String ip;
String port;
String apiKey;

DeviceArguments(this.deviceName, this.ip, this.port);
DeviceArguments(this.deviceName, this.ip, this.apiKey);

DeviceArguments.fromMap(Map map) {
deviceName = map[deviceName];
ip = map[ip];
port = map[port];
apiKey = map[apiKey];
}
}
4 changes: 3 additions & 1 deletion lib/models/settingArguments.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
class SettingArguments {
int alertDuration;
String apiKey;
List<DefaultCommand> defaultCommands;

SettingArguments(this.defaultCommands, this.alertDuration);
SettingArguments(this.defaultCommands, this.alertDuration, this.apiKey, );

SettingArguments.fromMap(Map map) {
alertDuration = map[alertDuration];
apiKey = map[apiKey];
defaultCommands = map[defaultCommands];
}
}
Expand Down
28 changes: 14 additions & 14 deletions lib/screens/addDevice.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ class AddDevicePage extends StatefulWidget {
class _AddDevicePageState extends State<AddDevicePage> {
final _titleController = TextEditingController();
final _ipController = TextEditingController();
final _portController = TextEditingController();
final _apiKeyController = TextEditingController();
bool _isComposingTitle = false;
bool _isComposingIp = false;
bool _isComposingPort = false;
bool _isComposingApiKey = false;
String _titleField = 'Device name';
String _ipField = 'IP-Address';
String _portField = 'Port';
String _apiKeyField = 'API Key';

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -69,13 +69,13 @@ class _AddDevicePageState extends State<AddDevicePage> {
AccentColorOverride(
color: primaryColor,
child: TextField(
controller: _portController,
controller: _apiKeyController,
decoration: InputDecoration(
labelText: _portField,
labelText: _apiKeyField,
),
onChanged: (String text) {
setState(() {
_isComposingPort = text.trim().length > 0;
_isComposingApiKey = text.trim().length > 0;
});
},
),
Expand All @@ -90,11 +90,11 @@ class _AddDevicePageState extends State<AddDevicePage> {
onPressed: () {
_titleController.clear();
_ipController.clear();
_portController.clear();
_apiKeyController.clear();
setState(() {
_isComposingTitle = false;
_isComposingIp = false;
_isComposingPort = false;
_isComposingApiKey = false;
});
},
),
Expand All @@ -109,9 +109,9 @@ class _AddDevicePageState extends State<AddDevicePage> {
borderRadius: BorderRadius.all(Radius.circular(15.0)),
),
onPressed:
(_isComposingTitle && _isComposingIp && _isComposingPort)
(_isComposingTitle && _isComposingIp && _isComposingApiKey)
? () => _handleSubmitted(_titleController.text,
_ipController.text, _portController.text)
_ipController.text, _apiKeyController.text)
: null,
)
],
Expand All @@ -122,21 +122,21 @@ class _AddDevicePageState extends State<AddDevicePage> {
);
}

void _handleSubmitted(String title, String ip, String port) {
void _handleSubmitted(String title, String ip, String apiKey) {
_titleController.clear();
_ipController.clear();
_portController.clear();
_apiKeyController.clear();
setState(() {
_isComposingTitle = false;
_isComposingIp = false;
_isComposingPort = false;
_isComposingApiKey = false;
});
Navigator.pop(
context,
DeviceArguments(
title.trim(),
ip.trim(),
port.trim(),
apiKey.trim(),
),
);
}
Expand Down
60 changes: 32 additions & 28 deletions lib/screens/chooseDevice.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ import 'package:mmremotecontrol/shared/colors.dart';
import 'package:mmremotecontrol/models/deviceArguments.dart';
import 'package:mmremotecontrol/services/database.dart';

Future<List<DeviceArguments>> fetchDevicesFromDatabase() async {
var dbHelper = SqLite();
Future<List<DeviceArguments>> devices = dbHelper.getDevices();
return devices;
}

class StartPage extends StatefulWidget {
static const routeName = '/startPage';
Expand All @@ -33,7 +28,7 @@ class _StartPageState extends State<StartPage> {
fetchDevicesFromDatabase().then((List<DeviceArguments> devices) {
for (DeviceArguments device in devices) {
Card _newDevice =
_createDevice(device.deviceName, device.ip, device.port, false);
_createDevice(device, false);
_devicesTemp.add(_newDevice);
}
setState(() {
Expand Down Expand Up @@ -102,8 +97,7 @@ class _StartPageState extends State<StartPage> {
);
DeviceArguments _deviceArguments = result;

Card _newDevice = _createDevice(_deviceArguments.deviceName, _deviceArguments.ip,
_deviceArguments.port, true);
Card _newDevice = _createDevice(_deviceArguments, true);
_initializeDevice(_newDevice);
_initializeDefaultCommands(_deviceArguments.deviceName);
_initializeSettings(_deviceArguments.deviceName);
Expand Down Expand Up @@ -154,7 +148,7 @@ class _StartPageState extends State<StartPage> {
fetchDevicesFromDatabase().then((List<DeviceArguments> devices) {
for (DeviceArguments device in devices) {
Card _newDevice =
_createDevice(device.deviceName, device.ip, device.port, false);
_createDevice(device, false);
_devicesTemp.add(_newDevice);
}
setState(() {
Expand All @@ -163,9 +157,9 @@ class _StartPageState extends State<StartPage> {
});
}

Card _createDevice(String deviceName, String ip, String port, bool persist) {
Card _createDevice(DeviceArguments device, bool persist) {
if (persist) {
_persistDevice(deviceName, ip, port);
_persistDevice(device);
}
return Card(
clipBehavior: Clip.antiAlias,
Expand All @@ -186,31 +180,30 @@ class _StartPageState extends State<StartPage> {
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
deviceName,
textScaleFactor: 1.1,
device.deviceName,
textScaleFactor: 1.2,
style: TextStyle(
color: primaryColor,
),
),
SizedBox(height: 12.0),
Text('IP: ' + ip),
SizedBox(height: 8.0),
Text('Port: ' + port),
Text(
'IP: ' + device.ip,
textScaleFactor: 1.1,
),
SizedBox(height: 4.0),
],
),
),
),
onPressed: () {
Navigator.pushNamed(
context,
CurrentDevicePage.routeName,
arguments: DeviceArguments(
deviceName,
ip,
port,
),
);
fetchDeviceFromDatabase(device.deviceName).then((DeviceArguments newDevice){
Navigator.pushNamed(
context,
CurrentDevicePage.routeName,
arguments: newDevice,
);
});
},
),
),
Expand All @@ -223,7 +216,7 @@ class _StartPageState extends State<StartPage> {
),
tooltip: 'Delete device',
onPressed: () {
_deleteDeviceDialog(context, deviceName);
_deleteDeviceDialog(context, device.deviceName);
},
),
],
Expand All @@ -232,8 +225,7 @@ class _StartPageState extends State<StartPage> {
);
}

void _persistDevice(String deviceName, String ipAdress, String port) {
var device = DeviceArguments(deviceName, ipAdress, port);
void _persistDevice(DeviceArguments device) {
var dbHelper = SqLite();
dbHelper.saveDevice(device);
}
Expand All @@ -255,3 +247,15 @@ class _StartPageState extends State<StartPage> {
dbHelper.saveSetting(setting);
}
}

Future<List<DeviceArguments>> fetchDevicesFromDatabase() async {
var dbHelper = SqLite();
Future<List<DeviceArguments>> devices = dbHelper.getDevices();
return devices;
}

Future<DeviceArguments> fetchDeviceFromDatabase(String deviceName) async {
var dbHelper = SqLite();
Future<DeviceArguments> device = dbHelper.getDevice(deviceName);
return device;
}
5 changes: 5 additions & 0 deletions lib/screens/currentDevice/cdDatabaseAccess.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,8 @@ void updateBrightnessSetting(String deviceName, int brightnessValue) {
dbHelper.updateBrightnessSetting(deviceName, brightnessValue);
}

void updateApiKey(String deviceName, String apiKey) {
var dbHelper = SqLite();
dbHelper.updateApiKey(deviceName, apiKey);
}

0 comments on commit a222255

Please sign in to comment.