{"id":13596269,"url":"https://github.com/NielsMasdorp/Speculum-Android","last_synced_at":"2025-04-09T16:32:04.015Z","repository":{"id":83857794,"uuid":"48172143","full_name":"NielsMasdorp/Speculum-Android","owner":"NielsMasdorp","description":"[Not maintained] Android application powering a magic mirror.","archived":true,"fork":false,"pushed_at":"2017-04-10T06:49:25.000Z","size":23149,"stargazers_count":330,"open_issues_count":8,"forks_count":71,"subscribers_count":24,"default_branch":"master","last_synced_at":"2024-11-06T19:42:44.432Z","etag":null,"topics":["android","mirror","smart-home","voice-commands"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NielsMasdorp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-12-17T12:05:16.000Z","updated_at":"2024-10-07T04:45:34.000Z","dependencies_parsed_at":null,"dependency_job_id":"e2dea672-2577-46ff-97dd-96e20658894b","html_url":"https://github.com/NielsMasdorp/Speculum-Android","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NielsMasdorp%2FSpeculum-Android","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NielsMasdorp%2FSpeculum-Android/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NielsMasdorp%2FSpeculum-Android/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NielsMasdorp%2FSpeculum-Android/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NielsMasdorp","download_url":"https://codeload.github.com/NielsMasdorp/Speculum-Android/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248067856,"owners_count":21042362,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","mirror","smart-home","voice-commands"],"created_at":"2024-08-01T16:02:13.985Z","updated_at":"2025-04-09T16:31:59.004Z","avatar_url":"https://github.com/NielsMasdorp.png","language":"Java","funding_links":[],"categories":["Java"],"sub_categories":[],"readme":"![alt tag](http://i.imgur.com/4AyPrAu.jpg)\n\nThis is an Android application for a magic mirror.\nI had an old Nexus 7 (2012) lying around collecting dust and got the idea from [HomeMirror by Hannah Mitt](https://github.com/HannahMitt/HomeMirror).\n\nI started this project because I wanted to explore the MVP pattern for Android and Hannah's app did not have all the functionality I needed. Feel free to make this application look good on your own device (it was designed for the Nexus 7). Have any great ideas to make this more configurable or usable on more devices? \nSubmit a pull request!\n\n====\n###Mirror example\n[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/_nnadMunSlc/0.jpg)](https://www.youtube.com/watch?v=_nnadMunSlc)\n====\nThis is a setup by an user who combined Speculum with a motion detector app to turn the mirror on when you enter the room.\n\n###Application screens\n\n![alt tag](http://i.imgur.com/SMdJDOd.jpg)\n\nFeatures\n====\n* Date and time\n* Switch between two custom layouts (Verbose and Simple)\n* Weather powered by Dark Sky\n  * Metric or Imperial\n  * Wind information\n    * Temperature\n    * Speed\n    * Direction\n  * Humidity\n  * Pressure\n  * Visibility\n  * Four day forecast\n* Your upcoming Google Calendar event\n* One of the top posts of your favorite subreddit\n* Interact with the mirror via voice commands (pull up map of your home, ask for a joke, update the visible data or be creative and implement your own)\n\nHow do I get started\n====\n\n1. Clone project in Android Studio\n2. Select JDK8 in Project Settings\n3. Make nescessary adjustments for your device\n4. Go to [darksky.net/dev](https://darksky.net/dev/) and register\n5. Go to [Google Static Maps API](https://developers.google.com/maps/documentation/static-maps/) and register for a browser key (this is optional, only do this if you want to have the mirror show a maps view of your home on command)\n6. Rename the `keys-sample.xml` file in `/res/values/` to `keys.xml` (sample found below these steps)\n7. Run on device or generate .APK\n8. Turn on \"Stay Awake\" in Developer Options on your device\n9. If you turned on voice recognition change the Text to Speech language to English in the language options on your device\n\nExample `keys.xml`\n====\n\n```java\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n\u003cresources\u003e\n    \u003cstring name=\"forecast_api_key\"\u003eyour Dark Sky api key\u003c/string\u003e\n    \u003cstring name=\"static_maps_api_key\"\u003eyour static maps api key\u003c/string\u003e\n\u003c/resources\u003e\n```\n\nUpdate data with voice command\n====\n\nWhen you've turned on the voice command option in the setup screen you can talk to your device and make it update the data. To do this wake the device up by saying **hello magic mirror**, the device will then respond with **hello there, how can I help you?**. You can then speak commands, for example: say **update** and the data will refresh, if you don't say anything for a minute the device will go back to sleep by itself, you can also force this by saying **go to sleep**. There are also some other built in commands like **tell me a joke** which tells you a joke and **show my location** which pulls up a maps view of your home (if you did all the steps in the tutorial above). A small demonstration of the concept can be found below.\n\n[![Alt text for your video](http://img.youtube.com/vi/bRPGOPEYoYI/0.jpg)](https://www.youtube.com/watch?v=bRPGOPEYoYI)\n\n###Add your own commands\n\nAlthough this is a bit out of the scope for this document I had a hard time understanding and configuring the Pocketsphinx library so I figure this might come in handy when you want to add your own functions to this application.\nThe `SpeechRecognizer` in this application has two modes; listen to one keyphrase (the wake up phrase) and listen to a list of keywords. The keyphrase to wake up the device is pretty straight forward, if you want to edit the phrase go to `Constants.java`,  and change \n\n```java\npublic static final String KEYPHRASE = \"hello magic mirror\";\n```\n\nto anything you want. When this phrase is recognized the `SpeechRecognizer` will switch to listening to any of the commands in a grammar file you specified. I specified the `commands.gram` file in the initialization of the `SpeechRecognizer` as shown below.\n\n```java\n// Create grammar-based search for command recognition\nFile commands = new File(assetsDir, \"commands.gram\");\nrecognizer.addKeywordSearch(Constants.COMMANDS_SEARCH, commands);\n```\n\nThe actual grammar file can be found in `/assets/sync/commands.gram`, you can change the command list or create a new file in that folder and pass it to the `SpeechRecognizer` at initialization. The grammar file must have this format:\n\n```\nupdate/1e-1/\ngo to sleep/1e-40/\ntell me a joke/1e-40/\nshow my location/1e-40/\n```\nThe number between the `//` is the threshold for detecting the keywords, the general rule is; the longer the keyword the bigger the threshold (e.g. one-word phrases `/1e-1/` and four-word phrases `/1e-40/`) but you can experiment with these values yourself. Also, every word in the commands you choose **must** exist in the dictionary file, which can be found in `/assets/sync/cmudict-en-us.dict`.\n\nAfter you add your commands add them individually to `Constants.java` as well.\n\n```java\npublic static final String KEYPHRASE = \"hello magic mirror\";\npublic static final String UPDATE_PHRASE = \"update\";\npublic static final String SLEEP_PHRASE = \"go to sleep\";\npublic static final String JOKE_ON_PHRASE = \"tell me a joke\";\npublic static final String MAP_PHRASE = \"show my location\";\n```\n\nNow you set your `SpeechRecognizer` to listen to the commands list.\n\n```java\nrecognizer.stop();\n//second parameter is the time to listen specified in milliseconds\nrecognizer.startListening(Constants.COMMANDS_SEARCH, TimeUnit.MINUTES.toMillis(1);\n```\nWhenever the `SpeechRecognizer` recognizes a command I pass the command for processing.\n\n```java\n @Override\n public void onPartialResult(Hypothesis hypothesis) {\n    if (hypothesis == null)\n        return;\n    processCommand(hypothesis.getHypstr());\n}\n```\nNow you only have to assign your custom actions to the commands in the presenter. You can assign your own actions to the commands in the `processCommand()` method in the `MainPresenterImpl.java`.\n\n```java\n@Override\npublic void processCommand(String command) {\n     switch (command) {\n         case Constants.KEYPHRASE:\n            // start listening for commands\n            setListeningMode(Constants.COMMANDS_SEARCH);\n            break;\n         case Constants.SLEEP_PHRASE:\n             // listen to wake up phrase again\n             setListeningMode(Constants.KWS_SEARCH);\n             break;\n         case Constants.UPDATE_PHRASE:\n             // update data and keep restart recognizer to listen for keywords\n             setListeningMode(Constants.COMMANDS_SEARCH);\n             break;\n        }\n    }\n}\n```\n\nThe way you make the `SpeechRecognizer` listen to the wake up phrase is similar to the commands method only this time do not pass a time to listen value as parameter.\n\n```java\nrecognizer.stop();\nrecognizer.startListening(Constants.KWS_SEARCH);\n```\n\nAfter changing anything related to the commands **reinstall the application** wake up the device and enjoy!\n\n###Thresholds\nMore info can be found in the [Pocketsphinx  tutorial](http://cmusphinx.sourceforge.net/wiki/tutoriallm).\n\n###Other languages and dictionaries\nI have not experimented with this, however it is possible to create your own dictionary file. Please refer to the [CMU Sphinx tutorial](http://cmusphinx.sourceforge.net/wiki/tutorial).\n\nHow I made my mirror\n====\n\nSince pre-made two way mirrors are very expensive and hard to get by in my country (The Netherlands) I decided to make my own.\nI bought some polished transparent plexiglass (60x40cm and 4mm thick) and some two way mirror foil normally used on windows for privacy. I built a wooden frame around it and used some small pieces of wood to hold the tablet in place (I left an opening on the bottom for the charger and on the top to expose the microphone). I then painted the back of the plexiglass black and that's it!\n\nThanks to\n====\n\n#####[gijsdewit](https://github.com/gijsdewit) for **awesome** designs\n\n###Used libraries\n* [Android Design Library](http://developer.android.com/tools/support-library/index.html)\n* [Retrofit](https://github.com/square/retrofit)\n* [RxAndroid](https://github.com/ReactiveX/RxAndroid)\n* [Dagger2](http://google.github.io/dagger/)\n* [Butterknife](https://github.com/JakeWharton/butterknife)\n* [Assent](https://github.com/afollestad/assent)\n* [Material Dialogs](https://github.com/afollestad/material-dialogs)\n* [Picasso](http://square.github.io/picasso/)\n* [Pocketsphinx](http://cmusphinx.sourceforge.net/wiki/tutorialandroid)\n\nLicense\n====\n```\nThe MIT License (MIT)\n\nCopyright (c) 2016 Niels Masdorp\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNielsMasdorp%2FSpeculum-Android","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FNielsMasdorp%2FSpeculum-Android","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNielsMasdorp%2FSpeculum-Android/lists"}