├── connected_car_app
├── README.md
├── common
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── trentseed
│ │ │ └── bmw_rpi_ibus_controller
│ │ │ └── common
│ │ │ ├── ControllerMessage.java
│ │ │ ├── WakeLocker.java
│ │ │ ├── ConnectedThread.java
│ │ │ ├── IBUSPacket.java
│ │ │ ├── BluetoothInterface.java
│ │ │ ├── IBUSWrapper.java
│ │ │ └── VoiceCommand.java
│ ├── proguard-rules.pro
│ └── build.gradle
├── ibuswear
│ ├── .gitignore
│ ├── libs
│ │ └── gson-2.2.4.jar
│ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── keys.png
│ │ │ │ ├── bmw_emblem.png
│ │ │ │ ├── button_up.png
│ │ │ │ ├── button_down.png
│ │ │ │ ├── button_exit.png
│ │ │ │ ├── button_lock.png
│ │ │ │ ├── button_unlock.png
│ │ │ │ ├── button_voice.png
│ │ │ │ ├── button_volume.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── bmw_background.png
│ │ │ │ ├── button_sunroof.png
│ │ │ │ ├── about_background.png
│ │ │ │ ├── bmw_emblem_pressed.png
│ │ │ │ ├── button_driver_seat.png
│ │ │ │ ├── window_front_left.png
│ │ │ │ ├── window_front_right.png
│ │ │ │ ├── window_rear_left.png
│ │ │ │ └── window_rear_right.png
│ │ │ ├── drawable-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── drawable-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ ├── layout
│ │ │ │ ├── round.xml
│ │ │ │ ├── keys.xml
│ │ │ │ ├── exit.xml
│ │ │ │ ├── keys_lock.xml
│ │ │ │ ├── voice.xml
│ │ │ │ ├── volume.xml
│ │ │ │ ├── about.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── sunroof.xml
│ │ │ │ ├── window_action_up.xml
│ │ │ │ ├── driver_seat.xml
│ │ │ │ ├── keys_unlock.xml
│ │ │ │ ├── window_action_down.xml
│ │ │ │ ├── window_back_left.xml
│ │ │ │ ├── window_back_right.xml
│ │ │ │ ├── window_front_left.xml
│ │ │ │ ├── window_front_right.xml
│ │ │ │ ├── rect.xml
│ │ │ │ └── welcome.xml
│ │ │ ├── values-v11
│ │ │ │ └── styles.xml
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ ├── menu
│ │ │ │ └── activity_main.xml
│ │ │ └── values-v14
│ │ │ │ └── styles.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── trentseed
│ │ │ │ └── bmw_rpi_ibus_controller
│ │ │ │ └── wear
│ │ │ │ ├── FragmentKeys.java
│ │ │ │ ├── FragmentAbout.java
│ │ │ │ ├── FragmentVolume.java
│ │ │ │ ├── FragmentSunroof.java
│ │ │ │ ├── FragmentDriverSeat.java
│ │ │ │ ├── FragmentWindowBackLeft.java
│ │ │ │ ├── FragmentWindowBackRight.java
│ │ │ │ ├── FragmentWindowFrontLeft.java
│ │ │ │ ├── FragmentWindowFrontRight.java
│ │ │ │ ├── FragmentKeysLock.java
│ │ │ │ ├── FragmentKeysUnlock.java
│ │ │ │ ├── FragmentExit.java
│ │ │ │ ├── FragmentWindowBackLeftActionUp.java
│ │ │ │ ├── FragmentWindowFrontLeftActionUp.java
│ │ │ │ ├── FragmentWindowBackRightActionUp.java
│ │ │ │ ├── FragmentWindowFrontRightActionUp.java
│ │ │ │ ├── FragmentWindowBackLeftActionDown.java
│ │ │ │ ├── FragmentWindowFrontLeftActionDown.java
│ │ │ │ ├── FragmentWindowBackRightActionDown.java
│ │ │ │ ├── FragmentWindowFrontRightActionDown.java
│ │ │ │ ├── FragmentVolumeDown.java
│ │ │ │ ├── FragmentVolumeUp.java
│ │ │ │ ├── FragmentSunroofOpen.java
│ │ │ │ ├── FragmentSunroofClose.java
│ │ │ │ ├── FragmentDriverSeatBack.java
│ │ │ │ ├── FragmentDriverSeatForward.java
│ │ │ │ ├── ActivityMain.java
│ │ │ │ ├── FragmentWelcome.java
│ │ │ │ ├── FragmentVoiceCommand.java
│ │ │ │ └── IBUSGridPagerAdapter.java
│ │ │ └── AndroidManifest.xml
│ ├── proguard-rules.pro
│ └── build.gradle
├── settings.gradle
├── app
│ ├── lint.xml
│ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── gear.png
│ │ │ │ ├── lock.png
│ │ │ │ ├── maps.png
│ │ │ │ ├── media.png
│ │ │ │ ├── radio.png
│ │ │ │ ├── voice.png
│ │ │ │ ├── arrow_up.png
│ │ │ │ ├── bmw_logo.png
│ │ │ │ ├── btn_off.png
│ │ │ │ ├── btn_on.png
│ │ │ │ ├── devices.png
│ │ │ │ ├── packet.png
│ │ │ │ ├── rpi_logo.png
│ │ │ │ ├── unlock.png
│ │ │ │ ├── view_bg.png
│ │ │ │ ├── arrow_back.png
│ │ │ │ ├── arrow_down.png
│ │ │ │ ├── arrow_left.png
│ │ │ │ ├── background.png
│ │ │ │ ├── bmw_emblem.png
│ │ │ │ ├── dash_icon.png
│ │ │ │ ├── default_bg.jpg
│ │ │ │ ├── icon_lock.png
│ │ │ │ ├── tablet_bg.png
│ │ │ │ ├── bus_activity.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── icon_lights.png
│ │ │ │ ├── icon_sunroof.png
│ │ │ │ ├── icon_volume.png
│ │ │ │ ├── default_bg_ibus.jpg
│ │ │ │ ├── default_bg_old.jpg
│ │ │ │ ├── icon_driverseat.png
│ │ │ │ ├── arrow_back_pressed.png
│ │ │ │ ├── bmw_emblem_pressed.png
│ │ │ │ ├── bmw_logo_pressed.png
│ │ │ │ ├── default_bg_radio.jpg
│ │ │ │ ├── default_bg_status.jpg
│ │ │ │ ├── default_bg_windows.jpg
│ │ │ │ ├── icon_window_left.png
│ │ │ │ └── icon_window_right.png
│ │ │ ├── xml
│ │ │ │ └── accessory_filter.xml
│ │ │ ├── drawable-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── drawable-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values-sw600dp
│ │ │ │ └── dimens.xml
│ │ │ ├── values
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ ├── drawable
│ │ │ │ ├── bmw_emblem_selector.xml
│ │ │ │ └── button_back_selector.xml
│ │ │ ├── menu
│ │ │ │ └── activity_main.xml
│ │ │ ├── values-sw720dp-land
│ │ │ │ └── dimens.xml
│ │ │ ├── values-v11
│ │ │ │ └── styles.xml
│ │ │ ├── values-v14
│ │ │ │ └── styles.xml
│ │ │ └── layout
│ │ │ │ ├── devices_row.xml
│ │ │ │ ├── activity_devices.xml
│ │ │ │ ├── activity_ibus.xml
│ │ │ │ ├── activity_radio.xml
│ │ │ │ └── activity_main.xml
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── trentseed
│ │ │ └── bmw_rpi_ibus_controller
│ │ │ ├── AdapterDevices.java
│ │ │ ├── ActivityRadio.java
│ │ │ ├── ActivityIBUS.java
│ │ │ └── AdapterIBUS.java
│ └── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── local.properties
├── build.gradle
├── import-summary.txt
├── gradlew.bat
└── gradlew
├── connected_car_controller
├── LICENSE.txt
├── README.md
├── test
│ ├── __init__.py
│ └── unit
│ │ ├── __init__.py
│ │ ├── controllers
│ │ ├── __init__.py
│ │ ├── bmw
│ │ │ ├── __init__.py
│ │ │ └── e46_test.py
│ │ └── base_test.py
│ │ ├── interfaces
│ │ ├── __init__.py
│ │ ├── base_test.py
│ │ ├── bt_test.py
│ │ └── ibus_test.py
│ │ └── start_controller_test.py
├── controllers
│ ├── __init__.py
│ ├── bmw
│ │ ├── __init__.py
│ │ └── e46.py
│ ├── factory.py
│ └── base.py
├── interfaces
│ ├── __init__.py
│ ├── base.py
│ ├── canbus.py
│ └── bt.py
├── version.py
├── requirements.txt
├── .coveragerc
├── scripts
│ └── run_for_e46.sh
├── docker-compose.yml
├── etc
│ └── config.ini
├── Dockerfile
├── setup.py
├── start_controller.py
└── pavement.py
├── .gitignore
└── README.md
/connected_car_app/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/connected_car_controller/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/connected_car_controller/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/connected_car_controller/test/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/connected_car_app/common/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/connected_car_controller/controllers/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/connected_car_controller/interfaces/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/connected_car_controller/test/unit/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/connected_car_controller/controllers/bmw/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/connected_car_controller/test/unit/controllers/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/connected_car_controller/test/unit/interfaces/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/connected_car_controller/test/unit/controllers/bmw/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/connected_car_controller/version.py:
--------------------------------------------------------------------------------
1 | __version__ = '2.0.0'
2 |
--------------------------------------------------------------------------------
/connected_car_controller/test/unit/interfaces/base_test.py:
--------------------------------------------------------------------------------
1 | import unittest
--------------------------------------------------------------------------------
/connected_car_controller/test/unit/interfaces/bt_test.py:
--------------------------------------------------------------------------------
1 | import unittest
--------------------------------------------------------------------------------
/connected_car_controller/test/unit/interfaces/ibus_test.py:
--------------------------------------------------------------------------------
1 | import unittest
--------------------------------------------------------------------------------
/connected_car_app/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':ibuswear', ':common'
2 |
--------------------------------------------------------------------------------
/connected_car_controller/test/unit/controllers/base_test.py:
--------------------------------------------------------------------------------
1 | import unittest
--------------------------------------------------------------------------------
/connected_car_controller/test/unit/controllers/bmw/e46_test.py:
--------------------------------------------------------------------------------
1 | import unittest
--------------------------------------------------------------------------------
/connected_car_controller/test/unit/start_controller_test.py:
--------------------------------------------------------------------------------
1 | import unittest
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Android/bin/
2 | Android/gen/
3 | *.pyc
4 | *.class
5 | java/bin/
6 |
--------------------------------------------------------------------------------
/connected_car_app/app/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/connected_car_controller/requirements.txt:
--------------------------------------------------------------------------------
1 | pyserial==3.0.1
2 | pybluez==0.22
3 | paver==1.3.4
--------------------------------------------------------------------------------
/connected_car_controller/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | omit =
3 | /Library/*
4 |
5 | [report]
6 |
7 | [html]
--------------------------------------------------------------------------------
/connected_car_app/common/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | common
3 |
4 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/libs/gson-2.2.4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/libs/gson-2.2.4.jar
--------------------------------------------------------------------------------
/connected_car_app/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/gear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/gear.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/lock.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/maps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/maps.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/media.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/media.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/radio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/radio.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/voice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/voice.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/xml/accessory_filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/arrow_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/arrow_up.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/bmw_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/bmw_logo.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/btn_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/btn_off.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/btn_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/btn_on.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/devices.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/devices.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/packet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/packet.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/rpi_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/rpi_logo.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/unlock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/unlock.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/view_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/view_bg.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/arrow_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/arrow_back.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/arrow_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/arrow_down.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/arrow_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/arrow_left.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/background.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/bmw_emblem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/bmw_emblem.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/dash_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/dash_icon.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/default_bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/default_bg.jpg
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/icon_lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/icon_lock.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/tablet_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/tablet_bg.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/keys.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/keys.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/bus_activity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/bus_activity.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/icon_lights.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/icon_lights.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/icon_sunroof.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/icon_sunroof.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/icon_volume.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/icon_volume.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/default_bg_ibus.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/default_bg_ibus.jpg
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/default_bg_old.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/default_bg_old.jpg
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/icon_driverseat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/icon_driverseat.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/bmw_emblem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/bmw_emblem.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_up.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/arrow_back_pressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/arrow_back_pressed.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/bmw_emblem_pressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/bmw_emblem_pressed.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/bmw_logo_pressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/bmw_logo_pressed.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/default_bg_radio.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/default_bg_radio.jpg
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/default_bg_status.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/default_bg_status.jpg
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/default_bg_windows.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/default_bg_windows.jpg
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/icon_window_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/icon_window_left.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable-hdpi/icon_window_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/app/src/main/res/drawable-hdpi/icon_window_right.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_down.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_exit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_exit.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_lock.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_unlock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_unlock.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_voice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_voice.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_volume.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_volume.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/bmw_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/bmw_background.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_sunroof.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_sunroof.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/about_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/about_background.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/bmw_emblem_pressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/bmw_emblem_pressed.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_driver_seat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/button_driver_seat.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/window_front_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/window_front_left.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/window_front_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/window_front_right.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/window_rear_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/window_rear_left.png
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/drawable-hdpi/window_rear_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/HEAD/connected_car_app/ibuswear/src/main/res/drawable-hdpi/window_rear_right.png
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/values-sw600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
8 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
8 |
--------------------------------------------------------------------------------
/connected_car_app/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Mar 24 12:23:50 PDT 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
7 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable/bmw_emblem_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/drawable/button_back_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/menu/activity_main.xml:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/values-sw720dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 128dp
8 |
9 |
10 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/round.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Connected Car
5 | Settings
6 | Hello world!
7 | BMW E46 IBUS Communication
8 |
9 |
10 |
--------------------------------------------------------------------------------
/connected_car_controller/scripts/run_for_e46.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Executes the core application for the vehicle controller. This process
3 | # is run in the background, and does not block the terminal. All output is
4 | # redirected to '/dev/null'
5 | echo running 'nohup python start_controller.py bmw-e46 2>/dev/null 1>/dev/null &'
6 | nohup python start_controller.py bmw-e46 2>/dev/null 1>/dev/null &
7 |
--------------------------------------------------------------------------------
/connected_car_controller/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 | car_controller:
5 | container_name: car_controller
6 | privileged: true
7 | network_mode: host
8 | restart: always
9 | build:
10 | context: .
11 | volumes:
12 | - "./:/opt/car-controller"
13 | - "/var/run/sdp:/var/run/sdp"
14 | devices:
15 | - "/dev/ttyUSB0:/dev/ttyUSB0"
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/connected_car_app/common/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/connected_car_controller/etc/config.ini:
--------------------------------------------------------------------------------
1 | [interfaces.bluetooth]
2 | service_name = ConnectedCar
3 | service_uuid = 94f39d29-7d6d-437d-973b-fba39e49d4ee
4 | read_buffer_length = 2048
5 |
6 | [interfaces.ibus]
7 | baudrate = 9600
8 | port = /dev/ttyUSB0
9 | timeout = 1
10 | read_buffer_length = 9999
11 |
12 | [interfaces.canbus]
13 | baudrate = 38400
14 | port = /dev/cu.usbserial-113010821422
15 | timeout = 1
16 | read_buffer_length = 9999
17 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Connected Car
5 | Hello world!
6 | Settings
7 |
8 |
9 | Hello blank fragment
10 |
11 |
12 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 | 64dp
9 |
10 |
11 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/menu/activity_main.xml:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/connected_car_controller/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.7.2-stretch
2 |
3 | MAINTAINER Trent Seed
4 |
5 | RUN apt-get -y update
6 |
7 | RUN apt-get -y install build-essential bluez bluez-tools libbluetooth-dev
8 |
9 | WORKDIR /opt/car-controller
10 |
11 | COPY requirements.txt /opt/car-controller
12 | RUN pip3 install -r /opt/car-controller/requirements.txt
13 |
14 | COPY . /opt/car-controller
15 |
16 | CMD ["python3", "/opt/car-controller/start_controller.py", "bmw-e46"]
--------------------------------------------------------------------------------
/connected_car_controller/setup.py:
--------------------------------------------------------------------------------
1 | """
2 | The setup.py module is responsible for describing the metadata necessary
3 | for building and installing the Python project.
4 | """
5 | from distutils.core import setup
6 | from version import __version__
7 |
8 |
9 | setup(
10 | name='ConnectedCarController',
11 | version=__version__,
12 | packages=['controllers', 'interfaces', ],
13 | license=open('LICENSE.txt').read(),
14 | author='Trent Seed',
15 | author_email='hi@trentseed.com'
16 | )
17 |
--------------------------------------------------------------------------------
/connected_car_controller/controllers/factory.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 |
4 | from controllers.bmw.e46 import E46Controller
5 |
6 |
7 | LOGGER = logging.getLogger(__name__)
8 |
9 |
10 | class ControllerFactory(object):
11 | """Factory class to instantiate controllers provided the type."""
12 |
13 | @staticmethod
14 | def create(controller_type):
15 | if controller_type == 'bmw-e46':
16 | return E46Controller()
17 |
18 | LOGGER.error('controller not supported - %r', controller_type)
19 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/keys.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/connected_car_app/local.properties:
--------------------------------------------------------------------------------
1 | ## This file is automatically generated by Android Studio.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must *NOT* be checked into Version Control Systems,
5 | # as it contains information specific to your local configuration.
6 | #
7 | # Location of the SDK. This is only used by Gradle.
8 | # For customization when using a Version Control System, please read the
9 | # header note.
10 | #Fri Mar 22 14:35:30 PDT 2019
11 | ndk.dir=/Users/trentseed/Library/Android/sdk/ndk-bundle
12 | sdk.dir=/Users/trentseed/Library/Android/sdk
13 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/keys_lock.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/voice.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/volume.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/about.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/sunroof.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/window_action_up.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/driver_seat.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/keys_unlock.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/window_action_down.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/connected_car_app/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | jcenter()
5 | maven {
6 | url 'https://maven.google.com/'
7 | name 'Google'
8 | }
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.3.2'
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | maven {
19 | url 'https://maven.google.com/'
20 | name 'Google'
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/window_back_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/window_back_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/window_front_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/window_front_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentKeys.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | public class FragmentKeys extends Fragment {
10 | @Override
11 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
12 | Bundle savedInstanceState) {
13 | // Inflate the layout for this fragment
14 | return inflater.inflate(R.layout.keys, container, false);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentAbout.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | public class FragmentAbout extends Fragment {
10 | @Override
11 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
12 | Bundle savedInstanceState) {
13 |
14 | // Inflate the layout for this fragment
15 | return inflater.inflate(R.layout.about, container, false);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/connected_car_app/common/src/main/java/com/trentseed/bmw_rpi_ibus_controller/common/ControllerMessage.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.common;
2 |
3 | import com.google.gson.Gson;
4 |
5 | /**
6 | * Data structure that encapsulates ibus packets which are sent between Android and
7 | * Raspberry Pi controller.
8 | */
9 | public class ControllerMessage {
10 |
11 | String data;
12 |
13 | public ControllerMessage(){}
14 |
15 | public IBUSPacket[] getIBUSPackets(){
16 | try{
17 | return new Gson().fromJson(this.data, IBUSPacket[].class);
18 | }catch(Exception e){
19 | e.printStackTrace();
20 | return null;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/connected_car_app/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 |
6 | defaultConfig {
7 | applicationId "com.trentseed.bmw_rpi_ibus_controller"
8 | minSdkVersion 20
9 | targetSdkVersion 26
10 | }
11 |
12 | buildTypes {
13 | release {
14 | minifyEnabled false
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
16 | }
17 | }
18 | }
19 |
20 | dependencies {
21 | implementation 'com.google.code.gson:gson:2.8.2'
22 | wearApp project(':ibuswear')
23 | implementation project(':common')
24 | implementation 'androidx.recyclerview:recyclerview:1.0.0'
25 | }
26 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentVolume.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.app.Fragment;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | public class FragmentVolume extends Fragment {
10 | @Override
11 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
12 | Bundle savedInstanceState) {
13 | // Inflate the layout for this fragment
14 | ActivityMain.parent_page_id = IBUSGridPagerAdapter.PAGE_VOLUME_LANDING;
15 | return inflater.inflate(R.layout.volume, container, false);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentSunroof.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.app.Fragment;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | public class FragmentSunroof extends Fragment {
10 | @Override
11 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
12 | Bundle savedInstanceState) {
13 | // Inflate the layout for this fragment
14 | ActivityMain.parent_page_id = IBUSGridPagerAdapter.PAGE_SUNROOF_LANDING;
15 | return inflater.inflate(R.layout.sunroof, container, false);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentDriverSeat.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.app.Fragment;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | public class FragmentDriverSeat extends Fragment {
10 | @Override
11 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
12 | Bundle savedInstanceState) {
13 | // Inflate the layout for this fragment
14 | ActivityMain.parent_page_id = IBUSGridPagerAdapter.PAGE_DRIVER_SEAT_LANDING;
15 | return inflater.inflate(R.layout.driver_seat, container, false);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/trentseed/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentWindowBackLeft.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | public class FragmentWindowBackLeft extends Fragment {
10 | @Override
11 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
12 | Bundle savedInstanceState) {
13 | // Inflate the layout for this fragment
14 | ActivityMain.parent_page_id = IBUSGridPagerAdapter.PAGE_REAR_LEFT_LANDING;
15 | return inflater.inflate(R.layout.window_back_left, container, false);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentWindowBackRight.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | public class FragmentWindowBackRight extends Fragment {
10 | @Override
11 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
12 | Bundle savedInstanceState) {
13 | // Inflate the layout for this fragment
14 | ActivityMain.parent_page_id = IBUSGridPagerAdapter.PAGE_REAR_RIGHT_LANDING;
15 | return inflater.inflate(R.layout.window_back_right, container, false);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentWindowFrontLeft.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | public class FragmentWindowFrontLeft extends Fragment {
10 | @Override
11 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
12 | Bundle savedInstanceState) {
13 | // Inflate the layout for this fragment
14 | ActivityMain.parent_page_id = IBUSGridPagerAdapter.PAGE_DRIVER_LANDING;
15 | return inflater.inflate(R.layout.window_front_left, container, false);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentWindowFrontRight.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | public class FragmentWindowFrontRight extends Fragment {
10 | @Override
11 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
12 | Bundle savedInstanceState) {
13 | // Inflate the layout for this fragment
14 | ActivityMain.parent_page_id = IBUSGridPagerAdapter.PAGE_PASSENGER_LANDING;
15 | return inflater.inflate(R.layout.window_front_right, container, false);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 |
4 | android {
5 | compileSdkVersion 26
6 |
7 | defaultConfig {
8 | applicationId "com.trentseed.bmw_rpi_ibus_controller"
9 | minSdkVersion 23
10 | targetSdkVersion 26
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | implementation 'com.google.code.gson:gson:2.8.2'
24 | implementation 'com.google.android.support:wearable:2.4.0'
25 | compileOnly 'com.google.android.wearable:wearable:2.4.0'
26 | implementation project(':common')
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/rect.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/connected_car_app/common/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/connected_car_app/common/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 26
5 |
6 | defaultConfig {
7 | minSdkVersion 20
8 | targetSdkVersion 26
9 | versionCode 1
10 | versionName "1.0"
11 |
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 |
14 | }
15 |
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 |
23 | }
24 |
25 | dependencies {
26 | implementation fileTree(include: ['*.jar'], dir: 'libs')
27 | implementation 'com.google.code.gson:gson:2.8.2'
28 | testImplementation 'junit:junit:4.12'
29 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
30 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
31 | }
32 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/res/layout/welcome.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentKeysLock.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentKeysLock extends Fragment {
13 |
14 | ImageView imgBtnLock;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | View rootView = inflater.inflate(R.layout.keys_lock, container, false);
21 | imgBtnLock = rootView.findViewById(R.id.imgBtnLock);
22 | imgBtnLock.setOnClickListener(new View.OnClickListener() {
23 | @Override
24 | public void onClick(View v) {
25 | try{
26 | IBUSWrapper.lockCar();
27 | } catch (Exception e){
28 | e.printStackTrace();
29 | }
30 | }
31 | });
32 | return rootView;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentKeysUnlock.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentKeysUnlock extends Fragment {
13 |
14 | ImageView imgBtnUnlock;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | View rootView = inflater.inflate(R.layout.keys_unlock, container, false);
21 | imgBtnUnlock = rootView.findViewById(R.id.imgBtnUnlock);
22 | imgBtnUnlock.setOnClickListener(new View.OnClickListener() {
23 | @Override
24 | public void onClick(View v) {
25 | try{
26 | IBUSWrapper.unlockCar();
27 | } catch (Exception e){
28 | e.printStackTrace();
29 | }
30 | }
31 | });
32 | return rootView;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentExit.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.app.Fragment;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.BluetoothInterface;
11 |
12 | public class FragmentExit extends Fragment {
13 |
14 | ImageView imgBtnClose;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 |
20 | // Inflate the layout for this fragment
21 | View rootView = inflater.inflate(R.layout.exit, container, false);
22 | imgBtnClose = rootView.findViewById(R.id.imgBtnExit);
23 | imgBtnClose.setOnClickListener(new View.OnClickListener() {
24 | @Override
25 | public void onClick(View v) {
26 | BluetoothInterface.disconnect();
27 | getActivity().finish();
28 | }
29 | });
30 |
31 | return rootView;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentWindowBackLeftActionUp.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentWindowBackLeftActionUp extends Fragment {
13 |
14 | ImageView imgBtnUp;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | View rootView = inflater.inflate(R.layout.window_action_up, container, false);
21 | imgBtnUp = rootView.findViewById(R.id.btn_up);
22 | imgBtnUp.setOnClickListener(new View.OnClickListener() {
23 | @Override
24 | public void onClick(View v) {
25 | try{
26 | IBUSWrapper.windowDriverRear(false);
27 | }catch (Exception e){
28 | e.printStackTrace();
29 | }
30 | }
31 | });
32 | return rootView;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentWindowFrontLeftActionUp.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentWindowFrontLeftActionUp extends Fragment {
13 |
14 | ImageView imgBtnUp;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | View rootView = inflater.inflate(R.layout.window_action_up, container, false);
21 | imgBtnUp = rootView.findViewById(R.id.btn_up);
22 | imgBtnUp.setOnClickListener(new View.OnClickListener() {
23 | @Override
24 | public void onClick(View v) {
25 | try{
26 | IBUSWrapper.windowDriverFront(false);
27 | }catch (Exception e){
28 | e.printStackTrace();
29 | }
30 | }
31 | });
32 | return rootView;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentWindowBackRightActionUp.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentWindowBackRightActionUp extends Fragment {
13 |
14 | ImageView imgBtnUp;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | View rootView = inflater.inflate(R.layout.window_action_up, container, false);
21 | imgBtnUp = rootView.findViewById(R.id.btn_up);
22 | imgBtnUp.setOnClickListener(new View.OnClickListener() {
23 | @Override
24 | public void onClick(View v) {
25 | try{
26 | IBUSWrapper.windowPassengerRear(false);
27 | }catch (Exception e){
28 | e.printStackTrace();
29 | }
30 | }
31 | });
32 | return rootView;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentWindowFrontRightActionUp.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentWindowFrontRightActionUp extends Fragment {
13 |
14 | ImageView imgBtnUp;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | View rootView = inflater.inflate(R.layout.window_action_up, container, false);
21 | imgBtnUp = rootView.findViewById(R.id.btn_up);
22 | imgBtnUp.setOnClickListener(new View.OnClickListener() {
23 | @Override
24 | public void onClick(View v) {
25 | try{
26 | IBUSWrapper.windowPassengerFront(false);
27 | }catch (Exception e){
28 | e.printStackTrace();
29 | }
30 | }
31 | });
32 | return rootView;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentWindowBackLeftActionDown.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentWindowBackLeftActionDown extends Fragment {
13 |
14 | ImageView imgBtnDown;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | View rootView = inflater.inflate(R.layout.window_action_down, container, false);
21 | imgBtnDown = rootView.findViewById(R.id.btn_down);
22 | imgBtnDown.setOnClickListener(new View.OnClickListener() {
23 | @Override
24 | public void onClick(View v) {
25 | try{
26 | IBUSWrapper.windowDriverRear(true);
27 | }catch (Exception e){
28 | e.printStackTrace();
29 | }
30 | }
31 | });
32 | return rootView;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentWindowFrontLeftActionDown.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentWindowFrontLeftActionDown extends Fragment {
13 |
14 | ImageView imgBtnDown;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | View rootView = inflater.inflate(R.layout.window_action_down, container, false);
21 | imgBtnDown = rootView.findViewById(R.id.btn_down);
22 | imgBtnDown.setOnClickListener(new View.OnClickListener() {
23 | @Override
24 | public void onClick(View v) {
25 | try{
26 | IBUSWrapper.windowDriverFront(true);
27 | }catch (Exception e){
28 | e.printStackTrace();
29 | }
30 | }
31 | });
32 | return rootView;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentWindowBackRightActionDown.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentWindowBackRightActionDown extends Fragment {
13 |
14 | ImageView imgBtnDown;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | View rootView = inflater.inflate(R.layout.window_action_down, container, false);
21 | imgBtnDown = rootView.findViewById(R.id.btn_down);
22 | imgBtnDown.setOnClickListener(new View.OnClickListener() {
23 | @Override
24 | public void onClick(View v) {
25 | try{
26 | IBUSWrapper.windowPassengerRear(true);
27 | }catch (Exception e){
28 | e.printStackTrace();
29 | }
30 | }
31 | });
32 | return rootView;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentWindowFrontRightActionDown.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.Bundle;
4 | import android.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentWindowFrontRightActionDown extends Fragment {
13 |
14 | ImageView imgBtnDown;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | View rootView = inflater.inflate(R.layout.window_action_down, container, false);
21 | imgBtnDown = rootView.findViewById(R.id.btn_down);
22 | imgBtnDown.setOnClickListener(new View.OnClickListener() {
23 | @Override
24 | public void onClick(View v) {
25 | try{
26 | IBUSWrapper.windowPassengerFront(true);
27 | }catch (Exception e){
28 | e.printStackTrace();
29 | }
30 | }
31 | });
32 | return rootView;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentVolumeDown.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.app.Fragment;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentVolumeDown extends Fragment {
13 |
14 | ImageView imgBtnDown;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | ActivityMain.parent_page_id = IBUSGridPagerAdapter.PAGE_VOLUME_DOWN;
21 | View rootView = inflater.inflate(R.layout.window_action_down, container, false);
22 | imgBtnDown = rootView.findViewById(R.id.btn_down);
23 | imgBtnDown.setOnClickListener(new View.OnClickListener() {
24 | @Override
25 | public void onClick(View v) {
26 | try{
27 | IBUSWrapper.volumeDown();
28 | }catch (Exception e){
29 | e.printStackTrace();
30 | }
31 | }
32 | });
33 | return rootView;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentVolumeUp.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.app.Fragment;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentVolumeUp extends Fragment {
13 |
14 | ImageView imgBtnUp;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | ActivityMain.parent_page_id = IBUSGridPagerAdapter.PAGE_VOLUME_UP;
21 | View rootView = inflater.inflate(R.layout.window_action_up, container, false);
22 | imgBtnUp = rootView.findViewById(R.id.btn_up);
23 | imgBtnUp.setOnClickListener(new View.OnClickListener() {
24 | @Override
25 | public void onClick(View v) {
26 | try{
27 | IBUSWrapper.volumeUp();
28 | }catch (Exception e){
29 | e.printStackTrace();
30 | }
31 | }
32 | });
33 | return rootView;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentSunroofOpen.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.app.Fragment;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentSunroofOpen extends Fragment {
13 |
14 | ImageView imgBtnUp;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | ActivityMain.parent_page_id = IBUSGridPagerAdapter.PAGE_SUNROOF_OPEN;
21 | View rootView = inflater.inflate(R.layout.window_action_up, container, false);
22 | imgBtnUp = rootView.findViewById(R.id.btn_up);
23 | imgBtnUp.setOnClickListener(new View.OnClickListener() {
24 | @Override
25 | public void onClick(View v) {
26 | try{
27 | IBUSWrapper.toggleSunroof(true);
28 | }catch (Exception e){
29 | e.printStackTrace();
30 | }
31 | }
32 | });
33 | return rootView;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentSunroofClose.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.app.Fragment;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentSunroofClose extends Fragment {
13 |
14 | ImageView imgBtnDown;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | ActivityMain.parent_page_id = IBUSGridPagerAdapter.PAGE_SUNROOF_CLOSE;
21 | View rootView = inflater.inflate(R.layout.window_action_down, container, false);
22 | imgBtnDown = rootView.findViewById(R.id.btn_down);
23 | imgBtnDown.setOnClickListener(new View.OnClickListener() {
24 | @Override
25 | public void onClick(View v) {
26 | try{
27 | IBUSWrapper.toggleSunroof(false);
28 | }catch (Exception e){
29 | e.printStackTrace();
30 | }
31 | }
32 | });
33 | return rootView;
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentDriverSeatBack.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.app.Fragment;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentDriverSeatBack extends Fragment {
13 |
14 | ImageView imgBtnDown;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | ActivityMain.parent_page_id = IBUSGridPagerAdapter.PAGE_DRIVER_SEAT_BACK;
21 | View rootView = inflater.inflate(R.layout.window_action_down, container, false);
22 | imgBtnDown = rootView.findViewById(R.id.btn_down);
23 | imgBtnDown.setOnClickListener(new View.OnClickListener() {
24 | @Override
25 | public void onClick(View v) {
26 | try{
27 | IBUSWrapper.moveDriverSeat(false);
28 | }catch (Exception e){
29 | e.printStackTrace();
30 | }
31 | }
32 | });
33 | return rootView;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentDriverSeatForward.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.app.Fragment;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
11 |
12 | public class FragmentDriverSeatForward extends Fragment {
13 |
14 | ImageView imgBtnUp;
15 |
16 | @Override
17 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState) {
19 | // Inflate the layout for this fragment
20 | ActivityMain.parent_page_id = IBUSGridPagerAdapter.PAGE_DRIVER_SEAT_FORWARD;
21 | View rootView = inflater.inflate(R.layout.window_action_up, container, false);
22 | imgBtnUp = rootView.findViewById(R.id.btn_up);
23 | imgBtnUp.setOnClickListener(new View.OnClickListener() {
24 | @Override
25 | public void onClick(View v) {
26 | try{
27 | IBUSWrapper.moveDriverSeat(true);
28 | }catch (Exception e){
29 | e.printStackTrace();
30 | }
31 | }
32 | });
33 | return rootView;
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/connected_car_controller/controllers/base.py:
--------------------------------------------------------------------------------
1 | """
2 | The controllers.base module contains logic common to all BaseController implementations.
3 | """
4 | import logging
5 | import sys
6 | import signal
7 | import time
8 |
9 |
10 | LOGGER = logging.getLogger(__name__)
11 |
12 |
13 | class ControllerStates(object):
14 | """The various states that an interface can experience."""
15 | STATE_STOPPED = 0
16 | STATE_RUNNING = 1
17 |
18 |
19 | class BaseController(object):
20 | """The base class for implementing a controller type."""
21 |
22 | __controller_name__ = None
23 | __states__ = ControllerStates
24 |
25 | def __init__(self):
26 | self.state = self.__states__.STATE_STOPPED
27 |
28 | def start(self):
29 | """Invoked when the controller is starting."""
30 | raise NotImplementedError
31 |
32 | def stop(self):
33 | """Invoked when the controller should stop."""
34 | raise NotImplementedError
35 |
36 | def restart(self):
37 | """Restarts the controller to establish fresh interface connections."""
38 | LOGGER.info('restarting the controller...')
39 | self.stop()
40 | self.start()
41 |
42 | @staticmethod
43 | def loop_until_ctrl_c():
44 | """Performs an infinite loop, waiting for "Ctrl-C" to exit the application."""
45 | LOGGER.info('press "ctrl-c" to exit the application...')
46 | signal.signal(signal.SIGINT, lambda x, y: sys.exit(0))
47 | while True:
48 | time.sleep(1)
49 |
50 |
51 |
--------------------------------------------------------------------------------
/connected_car_app/common/src/main/java/com/trentseed/bmw_rpi_ibus_controller/common/WakeLocker.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.common;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.os.PowerManager;
6 |
7 | import java.lang.NullPointerException;
8 |
9 | /**
10 | * This class handles device sleep/wake lock features. Utilized
11 | * by GCMIntentService.
12 | */
13 | public abstract class WakeLocker {
14 |
15 | private static PowerManager.WakeLock wakeLock;
16 | private final static String tag = "app:WakeLock";
17 |
18 | /**
19 | * Acquire handle and wake device
20 | * @param context
21 | */
22 | @SuppressWarnings("deprecation")
23 | @SuppressLint("Wakelock")
24 | public static void acquire(Context context) {
25 | if (wakeLock != null) release();
26 | PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
27 |
28 | try {
29 | wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK |
30 | PowerManager.ACQUIRE_CAUSES_WAKEUP |
31 | PowerManager.ON_AFTER_RELEASE, tag);
32 | wakeLock.acquire();
33 | }catch (NullPointerException e){
34 | e.printStackTrace();
35 | }
36 |
37 | }
38 |
39 | /**
40 | * Release handle, allowing device to sleep
41 | */
42 | private static void release() {
43 | if (wakeLock != null){
44 | wakeLock.release();
45 | wakeLock = null;
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/connected_car_controller/start_controller.py:
--------------------------------------------------------------------------------
1 | """
2 | The start_controller.py module serves as the main entry point to the connected car application.
3 | When this module is run from the command line, a signal handler will be attached to Ctrl+C so
4 | we can interrupt and exit the application.
5 |
6 | Example
7 | -------
8 | $ python3 start_controller.py bmw-e46
9 |
10 | """
11 | import logging
12 | import sys
13 | import argparse
14 |
15 |
16 | from controllers.factory import ControllerFactory
17 | from version import __version__
18 |
19 |
20 | LOGGER = logging.getLogger(__name__)
21 |
22 |
23 | def start_controller(controller_type):
24 | """
25 | Responsible for initializing a controller instance, which will subsequently initialize
26 | the necessary core services for the vehicle data bus and client devices e.g. bluetooth.
27 | """
28 | LOGGER.info('connected car controller - version: %s', __version__)
29 | controller = ControllerFactory.create(controller_type)
30 |
31 | if not controller:
32 | LOGGER.error("[ERROR] invalid controller, exiting...")
33 | sys.exit(2)
34 |
35 | controller.start()
36 |
37 |
38 | if __name__ == "__main__":
39 | # setup logging
40 | log_format = '%(asctime)s [%(levelname)s] [%(filename)s] [%(lineno)s] %(message)s'
41 | logging.basicConfig(level=logging.DEBUG, format=log_format)
42 |
43 | # parse command line arguments
44 | parser = argparse.ArgumentParser()
45 | parser.add_argument("controller_type", help="The name of the controller, for example 'bmw-e46'")
46 | args = parser.parse_args()
47 |
48 | # start the controller
49 | start_controller(args.controller_type)
50 |
--------------------------------------------------------------------------------
/connected_car_controller/controllers/bmw/e46.py:
--------------------------------------------------------------------------------
1 | """
2 | The e46.py module is responsible for BMW E46 vehicles.
3 |
4 | Car Production: March 1998 - February 2005
5 | Reference: https://en.wikipedia.org/wiki/BMW_3_Series_(E46)
6 |
7 | """
8 | import logging
9 |
10 | from controllers.base import BaseController
11 |
12 | from interfaces.ibus import IBUSInterface
13 | from interfaces.bt import BluetoothInterface
14 |
15 |
16 | LOGGER = logging.getLogger(__name__)
17 |
18 |
19 | class E46Controller(BaseController):
20 | """Controller definition for BMW E46 Vehicles"""
21 |
22 | __controller_name__ = 'bmw-e46'
23 |
24 | def __init__(self):
25 | super(E46Controller, self).__init__()
26 | self.bluetooth = BluetoothInterface(self) # interface available for clients
27 | self.ibus = IBUSInterface(self) # interface connected to the vehicle
28 |
29 | # bind interfaces to each other
30 | self.bluetooth.bind_receive(self.ibus.send)
31 | self.ibus.bind_receive(self.bluetooth.send)
32 |
33 | def start(self):
34 | """Invoked when the controller is starting."""
35 | LOGGER.info('starting the controller services...')
36 |
37 | # connect to the necessary interfaces
38 | self.ibus.connect()
39 | self.bluetooth.connect()
40 | self.state = self.__states__.STATE_RUNNING
41 | LOGGER.info('all services have been started')
42 |
43 | # run until "Ctrl-C" is pressed
44 | self.loop_until_ctrl_c()
45 |
46 | def stop(self):
47 | """Invoked when the controller should stop."""
48 | LOGGER.info('ending the controller services...')
49 | self.state = self.__states__.STATE_STOPPED
50 | self.ibus.disconnect()
51 | self.bluetooth.disconnect()
52 |
53 |
--------------------------------------------------------------------------------
/connected_car_controller/pavement.py:
--------------------------------------------------------------------------------
1 | """
2 | The pavement.py module defines various Paver tasks. Tasks include unit_tests, pylint, etc.
3 |
4 | Usage
5 | -----
6 | # execute the entire build pipeline
7 | $ paver
8 | ---> pavement.default
9 | ---> pavement.unit_tests
10 | nosetests --with-xcoverage --with-xunit --cover-package=controllers,interfaces --cover-erase
11 | ...
12 |
13 | # execute a particular task of the pipeline (method name)
14 | $ paver pylint
15 | ---> pavement.pylint
16 | pylint -f parseable controllers interfaces | tee pylint.out
17 | ...
18 |
19 | """
20 | from paver.tasks import BuildFailure, needs, task
21 | from paver.easy import sh
22 |
23 |
24 | @task
25 | def unit_tests():
26 | """Invoke nosetests runner for all test/unit/* unit tests."""
27 | settings = [
28 | '--with-xcoverage',
29 | '--with-xunit',
30 | '--cover-package=controllers,interfaces',
31 | '--cover-erase'
32 | ]
33 | sh('nosetests test/unit {}'.format(' '.join(settings)))
34 |
35 |
36 | @task
37 | def pylint():
38 | """Invoke pylint on remote_code_block and libs packages."""
39 | try:
40 | sh('pylint -f parseable --rcfile .pylintrc \
41 | controllers interfaces start_controller.py | tee pylint.out')
42 | except BuildFailure:
43 | pass # pylint errors should NOT fail the build
44 |
45 |
46 | @needs('unit_tests', 'pylint')
47 | @task
48 | def default():
49 | """The default task executed if `paver` is invoked with 0 arguments.
50 |
51 | A critical error in any of the stages will result in a FAILED build.
52 |
53 | Build Pipeline
54 | --------------
55 | - unit tests are exercised
56 | - behavioral feature tests are exercised
57 | - pylint scores the entire codebase
58 | - a deployment artifact is created
59 |
60 | """
61 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/layout/devices_row.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
19 |
20 |
28 |
29 |
36 |
37 |
43 |
44 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/ActivityMain.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.support.wearable.view.GridViewPager;
6 | import android.support.wearable.view.WatchViewStub;
7 | import android.view.Window;
8 | import android.view.WindowManager;
9 | import android.widget.Toast;
10 |
11 |
12 | public class ActivityMain extends Activity {
13 |
14 | /* track parent page id of first column of options (i.e. Driver Window, Locks, etc.) */
15 | public static int parent_page_id;
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | requestWindowFeature(Window.FEATURE_NO_TITLE);
21 |
22 | // disable action bar for swipe dismissal support
23 | getWindow().setFlags(
24 | WindowManager.LayoutParams.FLAG_FULLSCREEN,
25 | WindowManager.LayoutParams.FLAG_FULLSCREEN
26 | );
27 | setContentView(R.layout.activity_main);
28 |
29 | // inflate the 'rectangle' or 'round' view depending on device screen
30 | final WatchViewStub stub = findViewById(R.id.watch_view_stub);
31 | stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
32 | @Override
33 | public void onLayoutInflated(WatchViewStub stub) {
34 | // set adapter for 2D Picker pattern
35 | final GridViewPager pager = findViewById(R.id.pager);
36 | pager.setAdapter(
37 | new IBUSGridPagerAdapter(ActivityMain.this, getFragmentManager())
38 | );
39 | }
40 | });
41 | }
42 |
43 | /**
44 | * Display toast message for Toast.LENGTH_SHORT period. Uses the fragments
45 | * activity for context.
46 | * @param message content to display in toast message
47 | */
48 | public void showToast(String message){
49 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
35 |
37 |
39 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/connected_car_app/common/src/main/java/com/trentseed/bmw_rpi_ibus_controller/common/ConnectedThread.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.common;
2 |
3 | import java.io.IOException;
4 |
5 | import android.util.Log;
6 | import com.google.gson.Gson;
7 | import com.google.gson.JsonParser;
8 | import com.google.gson.JsonSyntaxException;
9 |
10 | /**
11 | * Thread that handles Bluetooth RFCOMM channel reading from Raspberry Pi
12 | */
13 | class ConnectedThread extends Thread {
14 |
15 | /**
16 | * Starts a loop responsible for continuously reading bytes from the
17 | * Bluetooth input stream, and invoking IBUSWrapper.processPacket().
18 | */
19 | public void run() {
20 |
21 | // start a loop that reads from the bluetooth input stream
22 | while (true) {
23 | try {
24 | // read bytes from bluetooth input stream
25 | byte[] buffer = new byte[2048]; // TODO: from config
26 | int bytes = BluetoothInterface.mBluetoothInputStream.read(buffer);
27 | if(bytes == 0) {
28 | continue;
29 | }
30 |
31 | // create String from data and validate JSON structure
32 | Log.d("BMW", "Data In = " + new String(buffer).trim());
33 | String strBuffer = new String(buffer).trim();
34 | if(!isJSONValid(strBuffer)) {
35 | continue;
36 | }
37 |
38 | // parse received data
39 | ControllerMessage msg = new Gson().fromJson(strBuffer, ControllerMessage.class);
40 | for(final IBUSPacket ibPacket : msg.getIBUSPackets()){
41 | BluetoothInterface.mActivity.runOnUiThread(new Runnable() {
42 | @Override
43 | public void run() {
44 | IBUSWrapper.processPacket(ibPacket);
45 | }
46 | });
47 | }
48 |
49 | } catch (IOException e) {
50 | Log.d("BMW", "Error reading: " + String.valueOf(e.getMessage()));
51 | break;
52 | }
53 | }
54 | }
55 |
56 | /**
57 | * Returns true or false is provided string input is JSON parsable.
58 | */
59 | private boolean isJSONValid(String json) {
60 | try {
61 | new JsonParser().parse(json);
62 | return true;
63 | } catch (JsonSyntaxException jse) {
64 | return false;
65 | }
66 | }
67 | }
68 |
69 |
--------------------------------------------------------------------------------
/connected_car_app/import-summary.txt:
--------------------------------------------------------------------------------
1 | ECLIPSE ANDROID PROJECT IMPORT SUMMARY
2 | ======================================
3 |
4 | Ignored Files:
5 | --------------
6 | The following files were *not* copied into the new Gradle project; you
7 | should evaluate whether these are still needed in your project and if
8 | so manually move them:
9 |
10 | * ic_launcher-web.png
11 | * proguard-project.txt
12 |
13 | Replaced Jars with Dependencies:
14 | --------------------------------
15 | The importer recognized the following .jar files as third party
16 | libraries and replaced them with Gradle dependencies instead. This has
17 | the advantage that more explicit version information is known, and the
18 | libraries can be updated automatically. However, it is possible that
19 | the .jar file in your project was of an older version than the
20 | dependency we picked, which could render the project not compileable.
21 | You can disable the jar replacement in the import wizard and try again:
22 |
23 | android-support-v4.jar => com.android.support:support-v4:19.1.0
24 | gson-2.2.4.jar => com.google.code.gson:gson:2.2.4
25 |
26 | Potentially Missing Dependency:
27 | -------------------------------
28 | When we replaced the following .jar files with a Gradle dependency, we
29 | inferred the dependency version number from the filename. This
30 | specific version may not actually be available from the repository.
31 | If you get a build error stating that the dependency is missing, edit
32 | the version number to for example "+" to pick up the latest version
33 | instead. (This may require you to update your code if the library APIs
34 | have changed.)
35 |
36 | gson-2.2.4.jar => version 2.2.4 in com.google.code.gson:gson:2.2.4
37 |
38 | Moved Files:
39 | ------------
40 | Android Gradle projects use a different directory structure than ADT
41 | Eclipse projects. Here's how the projects were restructured:
42 |
43 | * AndroidManifest.xml => app\src\main\AndroidManifest.xml
44 | * lint.xml => app\lint.xml
45 | * res\ => app\src\main\res\
46 | * src\ => app\src\main\java\
47 |
48 | Next Steps:
49 | -----------
50 | You can now build the project. The Gradle project needs network
51 | connectivity to download dependencies.
52 |
53 | Bugs:
54 | -----
55 | If for some reason your project does not build, and you determine that
56 | it is due to a bug or limitation of the Eclipse to Gradle importer,
57 | please file a bug at http://b.android.com with category
58 | Component-Tools.
59 |
60 | (This import summary is for your information only, and can be deleted
61 | after import once you are satisfied with the results.)
62 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentWelcome.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.os.AsyncTask;
4 | import android.os.Bundle;
5 | import android.app.Fragment;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.ImageView;
10 | import android.widget.ProgressBar;
11 |
12 | import com.trentseed.bmw_rpi_ibus_controller.common.BluetoothInterface;
13 |
14 | public class FragmentWelcome extends Fragment {
15 |
16 | ImageView imgBtnReconnect;
17 | ProgressBar pbConnectionLoader;
18 |
19 | @Override
20 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
21 | Bundle savedInstanceState) {
22 |
23 | // Inflate the layout for this fragment
24 | View rootView = inflater.inflate(R.layout.welcome, container, false);
25 | pbConnectionLoader = rootView.findViewById(R.id.pbConnectionLoader);
26 | imgBtnReconnect = rootView.findViewById(R.id.imgBtnEmblem);
27 | imgBtnReconnect.setOnClickListener(new View.OnClickListener() {
28 | @Override
29 | public void onClick(View v) {
30 | startReconnect();
31 | }
32 | });
33 |
34 | // start initial connect (if disconnected)
35 | if (!BluetoothInterface.isConnected()) startReconnect();
36 |
37 | return rootView;
38 | }
39 |
40 | /**
41 | * Starts the bluetooth connection process in a background thread.
42 | */
43 | public void startReconnect(){
44 | new ConnectViaBluetooth().execute();
45 | }
46 |
47 | /**
48 | * AsyncTask to perform the bluetooth connection.
49 | * Displays a progress bar loader to indicate progress.
50 | */
51 | private class ConnectViaBluetooth extends AsyncTask {
52 | @Override
53 | protected Void doInBackground(Void... params) {
54 | BluetoothInterface.checkConnection();
55 | return null;
56 | }
57 |
58 | @Override
59 | protected void onPostExecute(Void result) {
60 | ((ActivityMain)FragmentWelcome.this.getActivity())
61 | .showToast("Connected: " + BluetoothInterface.isConnected());
62 | pbConnectionLoader.setVisibility(View.GONE);
63 | }
64 |
65 | @Override
66 | protected void onPreExecute() {
67 | pbConnectionLoader.setVisibility(View.VISIBLE);
68 | }
69 |
70 | @Override
71 | protected void onProgressUpdate(Void... values) {
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/layout/activity_devices.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
17 |
18 |
21 |
22 |
30 |
31 |
41 |
42 |
43 |
44 |
51 |
52 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/FragmentVoiceCommand.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.app.Activity;
4 | import android.app.Fragment;
5 | import android.content.Intent;
6 | import android.os.Bundle;
7 | import android.speech.RecognizerIntent;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.ImageView;
12 |
13 | import java.util.List;
14 |
15 | import com.trentseed.bmw_rpi_ibus_controller.common.VoiceCommand;
16 |
17 | /**
18 | * https://developer.android.com/training/wearables/apps/voice.html
19 | */
20 | public class FragmentVoiceCommand extends Fragment {
21 |
22 | private static final int SPEECH_REQUEST_CODE = 0;
23 | ImageView imgVoiceCommand;
24 |
25 | @Override
26 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
27 |
28 | View rootView = inflater.inflate(R.layout.voice, container, false);
29 | imgVoiceCommand = rootView.findViewById(R.id.imgBtnExit);
30 | imgVoiceCommand.setOnClickListener(new View.OnClickListener() {
31 | @Override
32 | public void onClick(View v) {
33 | displaySpeechRecognizer();
34 | }
35 | });
36 |
37 | return rootView;
38 | }
39 |
40 | /**
41 | * Create an intent that can start the Speech Recognizer activity
42 | */
43 | private void displaySpeechRecognizer() {
44 | Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
45 | intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
46 | RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
47 | // Start the activity, the intent will be populated with the speech text
48 | startActivityForResult(intent, SPEECH_REQUEST_CODE);
49 | }
50 |
51 | /**
52 | * This callback is invoked when the Speech Recognizer returns.
53 | * This is where you process the intent and extract the speech text from the intent.
54 | */
55 | @Override
56 | public void onActivityResult(int requestCode, int resultCode, Intent data) {
57 | if (requestCode == SPEECH_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
58 | List results = data.getStringArrayListExtra( RecognizerIntent.EXTRA_RESULTS);
59 | String spokenText = results.get(0);
60 | String result = VoiceCommand.processSpokenText(spokenText);
61 | ((ActivityMain)this.getActivity()).showToast(result);
62 | }
63 | super.onActivityResult(requestCode, resultCode, data);
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/connected_car_app/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/connected_car_app/common/src/main/java/com/trentseed/bmw_rpi_ibus_controller/common/IBUSPacket.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.common;
2 |
3 | import java.text.Normalizer;
4 |
5 | /**
6 | * IBUS "packet" that is sent between BMW and Raspberry Pi
7 | */
8 | public class IBUSPacket {
9 |
10 | public String source_id;
11 | public String length;
12 | public String destination_id;
13 | public String data;
14 | public String xor_checksum;
15 | public String raw;
16 | public String timestamp;
17 |
18 | public IBUSPacket(){ }
19 |
20 | public String getSourceName(){
21 | return getDeviceName(this.source_id);
22 | }
23 |
24 | public String getDestinationName(){
25 | return getDeviceName(this.destination_id);
26 | }
27 |
28 | private String getDeviceName(String device_id){
29 | switch (device_id) {
30 | case "00":
31 | return "Broadcast 00";
32 | case "18":
33 | return "CDW - CDC CD-Player";
34 | case "3b":
35 | return "NAV Navigation/Video Module";
36 | case "43":
37 | return "Menu Screen";
38 | case "50":
39 | return "MFL Steering Wheel Controls";
40 | case "60":
41 | return "PDC Park Distance Control";
42 | case "68":
43 | return "RAD Radio";
44 | case "6a":
45 | return "DSP Digital Sound Processor";
46 | case "80":
47 | return "IKE Instrument Kombi Electronics";
48 | case "bb":
49 | return "TV Module";
50 | case "bf":
51 | return "LCM Light Control Module";
52 | case "c0":
53 | return "MID Multi-Information Display Buttons";
54 | case "c8":
55 | return "TEL Telephone";
56 | case "d0":
57 | return "Navigation Location";
58 | case "e7":
59 | return "OBC Text Bar";
60 | case "ed":
61 | return "Lights, Wipers, Seat Memory";
62 | case "f0":
63 | return "BMB Board Monitor Buttons";
64 | case "ff":
65 | return "Broadcast FF";
66 | default:
67 | return "Unknown";
68 | }
69 | }
70 |
71 | /**
72 | * Parses hex string and returns ASCII character representation
73 | * http://www.mkyong.com/java/how-to-convert-hex-to-ascii-in-java/
74 | * @return String
75 | */
76 | public String getAsciiFromRaw(){
77 | StringBuilder sb = new StringBuilder();
78 | for( int i=0; i
8 |
9 |
18 |
19 |
26 |
27 |
30 |
31 |
39 |
40 |
50 |
51 |
63 |
64 |
65 |
66 |
71 |
72 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/java/com/trentseed/bmw_rpi_ibus_controller/AdapterDevices.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 |
11 | import java.util.List;
12 |
13 | import androidx.recyclerview.widget.RecyclerView;
14 |
15 |
16 | public class AdapterDevices extends RecyclerView.Adapter {
17 |
18 | private List mData;
19 | private LayoutInflater mInflater;
20 |
21 | AdapterDevices(Context context, List data) {
22 | this.mInflater = LayoutInflater.from(context);
23 | this.mData = data;
24 | }
25 |
26 | @Override
27 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
28 | View view = mInflater.inflate(R.layout.devices_row, parent, false);
29 | return new ViewHolder(view);
30 | }
31 |
32 | @Override
33 | public void onBindViewHolder(ViewHolder holder, int position) {
34 | Device device = mData.get(position);
35 |
36 | // bind text values
37 | holder.deviceName.setText(device.name);
38 |
39 | // bind image resources
40 | if (device.icon > 0) {
41 | holder.deviceIcon.setImageResource(device.icon);
42 | }
43 | if (device.imageAction1 > 0) {
44 | holder.deviceAction1.setImageResource(device.imageAction1);
45 | }
46 | if (device.imageAction2 > 0) {
47 | holder.deviceAction2.setImageResource(device.imageAction2);
48 | }
49 |
50 | // bind click events
51 | if (device.onClickListenerAction1 != null){
52 | holder.deviceAction1.setOnClickListener(device.onClickListenerAction1);
53 | }
54 | if (device.onClickListenerAction2 != null){
55 | holder.deviceAction2.setOnClickListener(device.onClickListenerAction2);
56 | }
57 | }
58 |
59 | @Override
60 | public int getItemCount() {
61 | return mData.size();
62 | }
63 |
64 |
65 | // stores and recycles views as they are scrolled off screen
66 | public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
67 | TextView deviceName;
68 | ImageView deviceIcon;
69 | ImageView deviceAction1;
70 | ImageView deviceAction2;
71 |
72 | ViewHolder(View itemView) {
73 | super(itemView);
74 | deviceName = itemView.findViewById(R.id.tvDeviceName);
75 | deviceIcon = itemView.findViewById(R.id.ivDeviceIcon);
76 | deviceAction1 = itemView.findViewById(R.id.ivDeviceAction1);
77 | deviceAction2 = itemView.findViewById(R.id.ivDeviceAction2);
78 | itemView.setOnClickListener(this);
79 | }
80 |
81 | @Override
82 | public void onClick(View view) {
83 | Log.d("BMW", "onclick occurred in viewholder");
84 | }
85 |
86 | }
87 |
88 | public static class Device{
89 | int icon;
90 | String name;
91 | int imageAction1;
92 | int imageAction2;
93 | View.OnClickListener onClickListenerAction1;
94 | View.OnClickListener onClickListenerAction2;
95 |
96 | public Device(String name){
97 | this.name = name;
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/connected_car_app/common/src/main/java/com/trentseed/bmw_rpi_ibus_controller/common/BluetoothInterface.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.common;
2 |
3 | import java.io.InputStream;
4 | import java.io.OutputStream;
5 | import java.util.ArrayList;
6 | import java.util.List;
7 | import java.util.UUID;
8 |
9 | import android.app.Activity;
10 | import android.bluetooth.BluetoothAdapter;
11 | import android.bluetooth.BluetoothDevice;
12 | import android.bluetooth.BluetoothSocket;
13 | import android.util.Log;
14 | import android.widget.Toast;
15 |
16 | /**
17 | * Handles Bluetooth communication with Raspberry Pi
18 | *
19 | */
20 | public class BluetoothInterface {
21 |
22 | // bluetooth objects
23 | public static Activity mActivity;
24 | public static List mArrayListIBUSActivity = new ArrayList<>();
25 | public static BluetoothAdapter mBluetoothAdapter;
26 | public static BluetoothDevice mBluetoothDevice;
27 | public static BluetoothSocket mBluetoothSocket;
28 | public static InputStream mBluetoothInputStream;
29 | public static OutputStream mBluetoothOutputStream;
30 | public static UUID serviceUUID = UUID.fromString("94f39d29-7d6d-437d-973b-fba39e49d4ee");
31 | public static String remoteBluetoothAddress = "B8:27:EB:69:90:49";
32 | public static ConnectedThread listenThread;
33 | public static Boolean isConnecting = false;
34 |
35 | /**
36 | * Connects to Raspberry Pi via Bluetooth.
37 | * Note: Python services must be running on remote device.
38 | */
39 | public static void connectToRaspberryPi(){
40 | Log.d("BMW", "attempting to connect to controller...");
41 | try{
42 | // connect to device and get input stream
43 | BluetoothInterface.isConnecting = true;
44 | mArrayListIBUSActivity = new ArrayList<>();
45 | mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(remoteBluetoothAddress);
46 | mBluetoothSocket = mBluetoothDevice.createInsecureRfcommSocketToServiceRecord(serviceUUID);
47 | mBluetoothSocket.connect();
48 | mBluetoothInputStream = mBluetoothSocket.getInputStream();
49 | mBluetoothOutputStream = mBluetoothSocket.getOutputStream();
50 | BluetoothInterface.isConnecting = false;
51 |
52 | // start listening for data on new thread
53 | Log.d("BMW", "starting connected thread...");
54 | listenThread = new ConnectedThread();
55 | listenThread.start();
56 | }catch(Exception e){
57 | BluetoothInterface.isConnecting = true;
58 | Log.d("BMW", "exception connecting to controller: " + e.getMessage());
59 | // if(mActivity != null && !mActivity.isFinishing()) {
60 | // Toast.makeText(mActivity, "Unable To Connect via Bluetooth", Toast.LENGTH_LONG).show();
61 | // }
62 | }
63 | }
64 |
65 | /**
66 | * Determines if Bluetooth connection has been established with Raspberry Pi
67 | * @return boolean
68 | */
69 | public static boolean isConnected(){
70 | return mBluetoothAdapter != null && mBluetoothDevice != null && mBluetoothSocket.isConnected();
71 | }
72 |
73 | /**
74 | * Determines if Bluetooth is in progress with establishing connection
75 | * @return boolean
76 | */
77 | public static boolean isConnecting(){
78 | return isConnecting;
79 | }
80 |
81 | /**
82 | * Checks Bluetooth connection, and connects if necessary.
83 | */
84 | public static void checkConnection(){
85 | if(!BluetoothInterface.isConnected()){
86 | BluetoothInterface.mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
87 | BluetoothInterface.connectToRaspberryPi();
88 | }
89 | }
90 |
91 | /**
92 | * Disconnect Bluetooth RFCOMM connection
93 | */
94 | public static void disconnect(){
95 | try{
96 | mBluetoothSocket.close();
97 | }catch(Exception e){
98 | Log.d("BMW", e.getMessage());
99 | }finally {
100 | mBluetoothAdapter = null;
101 | mBluetoothDevice = null;
102 | mBluetoothSocket = null;
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/java/com/trentseed/bmw_rpi_ibus_controller/ActivityRadio.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.view.View;
6 | import android.view.Window;
7 | import android.view.WindowManager;
8 | import android.widget.Button;
9 | import android.widget.ImageView;
10 |
11 | import com.trentseed.bmw_rpi_ibus_controller.common.BluetoothInterface;
12 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSWrapper;
13 |
14 | public class ActivityRadio extends Activity {
15 |
16 | ImageView ivBack;
17 | Button btnAccept;
18 | Button btnVolUp;
19 | Button btnVolDown;
20 | Button btnRadioPower;
21 | Button btnDriverSeatForward;
22 | Button btnDriverSeatBack;
23 | Button btnSunroofOpen;
24 | Button btnSunroofClose;
25 | Button btnLock;
26 | Button btnUnlock;
27 | Button btnMode;
28 |
29 | @Override
30 | protected void onCreate(Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 | requestWindowFeature(Window.FEATURE_NO_TITLE);
33 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
34 | onNewIntent(getIntent());
35 | setContentView(R.layout.activity_radio);
36 | BluetoothInterface.mActivity = this;
37 |
38 | // get layout objects
39 | ivBack = findViewById(R.id.ivBack);
40 | btnAccept = findViewById(R.id.btnAccept);
41 | btnVolUp = findViewById(R.id.btnVolUp);
42 | btnVolDown = findViewById(R.id.btnVolDown);
43 | btnRadioPower = findViewById(R.id.btnRadioPower);
44 | btnDriverSeatForward = findViewById(R.id.btnDriverSeatForward);
45 | btnDriverSeatBack = findViewById(R.id.btnDriverSeatBack);
46 | btnSunroofOpen = findViewById(R.id.btnSunroofOpen);
47 | btnSunroofClose = findViewById(R.id.btnSunroofClose);
48 | btnLock = findViewById(R.id.btnLock);
49 | btnUnlock = findViewById(R.id.btnUnlock);
50 | btnMode = findViewById(R.id.btnMode);
51 |
52 | // set click handlers
53 | ivBack.setOnClickListener(new View.OnClickListener() {
54 | @Override
55 | public void onClick(View v) {
56 | finish();
57 | }
58 | });
59 | btnAccept.setOnClickListener(new View.OnClickListener() {
60 | @Override
61 | public void onClick(View v) {
62 | IBUSWrapper.pressAccept();
63 | }
64 | });
65 | btnVolUp.setOnClickListener(new View.OnClickListener() {
66 | @Override
67 | public void onClick(View v) {
68 | IBUSWrapper.volumeUp();
69 | }
70 | });
71 | btnVolDown.setOnClickListener(new View.OnClickListener() {
72 | @Override
73 | public void onClick(View v) {
74 | IBUSWrapper.volumeDown();
75 | }
76 | });
77 | btnRadioPower.setOnClickListener(new View.OnClickListener() {
78 | @Override
79 | public void onClick(View v) {
80 | IBUSWrapper.toggleRadioPower();
81 | }
82 | });
83 | btnMode.setOnClickListener(new View.OnClickListener() {
84 | @Override
85 | public void onClick(View v) {
86 | IBUSWrapper.toggleMode(ActivityRadio.this);
87 | }
88 | });
89 | btnDriverSeatForward.setOnClickListener(new View.OnClickListener() {
90 | @Override
91 | public void onClick(View v) {
92 | IBUSWrapper.moveDriverSeat(true);
93 | }
94 | });
95 | btnDriverSeatBack.setOnClickListener(new View.OnClickListener() {
96 | @Override
97 | public void onClick(View v) {
98 | IBUSWrapper.moveDriverSeat(false);
99 | }
100 | });
101 | btnLock.setOnClickListener(new View.OnClickListener() {
102 | @Override
103 | public void onClick(View v) {
104 | IBUSWrapper.lockCar();
105 | }
106 | });
107 | btnUnlock.setOnClickListener(new View.OnClickListener() {
108 | @Override
109 | public void onClick(View v) {
110 | IBUSWrapper.unlockCar();
111 | }
112 | });
113 | btnSunroofOpen.setOnClickListener(new View.OnClickListener() {
114 | @Override
115 | public void onClick(View v) {
116 | IBUSWrapper.toggleSunroof(true);
117 | }
118 | });
119 | btnSunroofClose.setOnClickListener(new View.OnClickListener() {
120 | @Override
121 | public void onClick(View v) {
122 | IBUSWrapper.toggleSunroof(false);
123 | }
124 | });
125 | }
126 |
127 | @Override
128 | protected void onResume(){
129 | super.onResume();
130 | BluetoothInterface.mActivity = this;
131 | BluetoothInterface.checkConnection();
132 | }
133 |
134 | }
135 |
--------------------------------------------------------------------------------
/connected_car_controller/interfaces/base.py:
--------------------------------------------------------------------------------
1 | """
2 | The interfaces.base module contains logic common to all BaseInterface implementations.
3 | """
4 | import logging
5 | import os
6 | from configparser import ConfigParser
7 |
8 |
9 | LOGGER = logging.getLogger(__name__)
10 |
11 |
12 | class InterfaceStates(object):
13 | """The various states that an interface can experience."""
14 | STATE_READY = 0
15 | STATE_CONNECTING = 1
16 | STATE_CONNECTED = 2
17 | STATE_DISCONNECTING = 3
18 |
19 |
20 | class BaseInterface(object):
21 | """The base class for implementing an interface type."""
22 |
23 | __interface_name__ = None
24 | __states__ = InterfaceStates
25 |
26 | def __init__(self):
27 | self.state = self.__states__.STATE_READY
28 | self.send_hook = None
29 | self.receive_hook = None
30 |
31 | def get_setting(self, setting, setting_type=str):
32 | """
33 | Returns a specified setting from etc/config.ini.
34 |
35 | Parameters
36 | ----------
37 | setting : basestring
38 | the key of the setting to return under the [interfaces.{__interface_name__}] section
39 | setting_type : type
40 | [optional] the data type for the setting e.g. int or bool. default is basestring.
41 |
42 | Returns
43 | -------
44 | basestring
45 | string value of the setting
46 |
47 | """
48 | path = os.path.join(
49 | os.path.dirname(os.path.abspath(__file__)), '..', 'etc', 'config.ini'
50 | )
51 | parser = ConfigParser() # TODO: verify works; was SafeConfigParser
52 | parser.read(path)
53 | section = 'interfaces.{}'.format(self.__interface_name__)
54 |
55 | # return setting value using the specified setting_type
56 | if setting_type is int:
57 | return parser.getint(section, setting)
58 | elif setting_type is bool:
59 | return parser.getboolean(section, setting)
60 | elif setting_type is float:
61 | return parser.getfloat(section, setting)
62 | else:
63 | return parser.get(section, setting)
64 |
65 | def connect(self):
66 | """Invoked when the interface is performing a connection"""
67 | raise NotImplementedError
68 |
69 | def disconnect(self):
70 | """Invoked when the interface is being destroyed"""
71 | raise NotImplementedError
72 |
73 | def send(self, data):
74 | """Invoked when data should be sent over the interface
75 |
76 | Arguments
77 | ---------
78 | data : basestring
79 | the data to be sent via this interface
80 |
81 | """
82 | raise NotImplementedError
83 |
84 | def receive(self, data):
85 | """Invoked when data is received from the interface
86 |
87 | Arguments
88 | ---------
89 | data : basestring
90 | incoming data received from this interface
91 |
92 | """
93 | raise NotImplementedError
94 |
95 | def consume_bus(self):
96 | """Responsible for initiating a loop and reading from the bus."""
97 | raise NotImplementedError
98 |
99 | def reconnect(self):
100 | """Performs a reconnect job to establish a fresh connection"""
101 | LOGGER.info('restarting interface...')
102 | self.disconnect()
103 | self.connect()
104 |
105 | def bind_receive(self, method):
106 | """Used to bind a method that will be called every time the interface receives data.
107 |
108 | Arguments
109 | ---------
110 | method : function
111 | a method to bind and invoke each time the interface receives data
112 |
113 | """
114 | if not hasattr(method, '__call__'):
115 | LOGGER.error('error in bind_receive: method must be callable!')
116 | return
117 |
118 | self.receive_hook = method
119 |
120 | def bind_send(self, method):
121 | """Used to bind a method that will be called every time the interface sends data.
122 |
123 | Arguments
124 | ---------
125 | method : function
126 | a method to bind and invoke each time the interface sends data
127 |
128 | """
129 | if not hasattr(method, '__call__'):
130 | LOGGER.error('error in bind_send: method must be callable!')
131 | return
132 |
133 | self.send_hook = method
134 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/java/com/trentseed/bmw_rpi_ibus_controller/ActivityIBUS.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.content.DialogInterface;
6 | import android.os.Bundle;
7 | import android.view.View;
8 | import android.view.Window;
9 | import android.view.WindowManager;
10 | import android.view.animation.AccelerateInterpolator;
11 | import android.view.animation.AlphaAnimation;
12 | import android.view.animation.Animation;
13 | import android.view.animation.Animation.AnimationListener;
14 | import android.widget.AbsListView;
15 | import android.widget.AdapterView;
16 | import android.widget.ImageView;
17 | import android.widget.ListView;
18 | import android.widget.TextView;
19 |
20 | import com.trentseed.bmw_rpi_ibus_controller.common.BluetoothInterface;
21 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSPacket;
22 |
23 | public class ActivityIBUS extends Activity {
24 |
25 | // layout objects
26 | ImageView ivBack;
27 | ImageView ivBusActivity;
28 | ListView lvBusEvents;
29 | TextView tvNoActivity;
30 | AdapterIBUS adapter;
31 |
32 | @Override
33 | protected void onCreate(Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 | requestWindowFeature(Window.FEATURE_NO_TITLE);
36 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
37 | onNewIntent(getIntent());
38 | setContentView(R.layout.activity_ibus);
39 | BluetoothInterface.mActivity = this;
40 |
41 | // get layout objects
42 | ivBack = findViewById(R.id.ivBack);
43 | ivBusActivity = findViewById(R.id.ivBusActivity);
44 | ivBusActivity.setVisibility(View.GONE);
45 | lvBusEvents = findViewById(R.id.lvBusEvents);
46 | tvNoActivity = findViewById(R.id.tvNoActivity);
47 |
48 | // set click handlers
49 | ivBack.setOnClickListener(new View.OnClickListener() {
50 | @Override
51 | public void onClick(View v) {
52 | finish();
53 | }
54 | });
55 |
56 | // create and set adapter
57 | adapter = new AdapterIBUS(this);
58 | lvBusEvents.setAdapter(adapter);
59 | if(adapter.getCount() > 0) tvNoActivity.setVisibility(View.GONE);
60 | lvBusEvents.setOnItemClickListener(new AbsListView.OnItemClickListener() {
61 | @Override
62 | public void onItemClick(AdapterView> arg0, View arg1, int position, long arg3) {
63 | IBUSPacket ibPacket = adapter.getItem(position);
64 | AlertDialog.Builder msgBuilder = new AlertDialog.Builder(ActivityIBUS.this);
65 | msgBuilder.setTitle("IBUS Packet");
66 | msgBuilder.setMessage("Data: " + ibPacket.raw + "\nASCII: " + ibPacket.getAsciiFromRaw());
67 | msgBuilder.setPositiveButton("Done", new DialogInterface.OnClickListener() {
68 | @Override
69 | public void onClick(DialogInterface dialog, int which) {}
70 | });
71 | msgBuilder.create().show();
72 |
73 | }
74 | });
75 | }
76 |
77 | /**
78 | * Received an IBUSPacket update, refresh ListView
79 | */
80 | public void receivedIBUSPacket(IBUSPacket ibPacket){
81 | adapter.notifyDataSetChanged();
82 | flashActivity();
83 | }
84 |
85 | /**
86 | * Indicate IBUS activity on UI by "flashing" green indicator.
87 | */
88 | private void flashActivity(){
89 | // fade in animation
90 | ivBusActivity.setVisibility(View.VISIBLE);
91 | Animation fadeIn = new AlphaAnimation(0, 1);
92 | fadeIn.setInterpolator(new AccelerateInterpolator());
93 | fadeIn.setDuration(300);
94 | fadeIn.setAnimationListener(new AnimationListener(){
95 | public void onAnimationEnd(Animation animation){
96 | // fade out animation
97 | Animation fadeOut = new AlphaAnimation(1, 0);
98 | fadeOut.setInterpolator(new AccelerateInterpolator());
99 | fadeOut.setDuration(300);
100 | fadeOut.setAnimationListener(new AnimationListener(){
101 | public void onAnimationEnd(Animation animation){
102 | ivBusActivity.setVisibility(View.GONE);
103 | }
104 | public void onAnimationRepeat(Animation animation){ }
105 | public void onAnimationStart(Animation animation){ }
106 | });
107 | ivBusActivity.setVisibility(View.VISIBLE);
108 | ivBusActivity.startAnimation(fadeOut);
109 | }
110 | public void onAnimationRepeat(Animation animation){ }
111 | public void onAnimationStart(Animation animation){ }
112 | });
113 | ivBusActivity.startAnimation(fadeIn);
114 | }
115 |
116 | @Override
117 | protected void onResume(){
118 | super.onResume();
119 | BluetoothInterface.mActivity = this;
120 | BluetoothInterface.checkConnection();
121 | }
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/connected_car_controller/interfaces/canbus.py:
--------------------------------------------------------------------------------
1 | """
2 | This module contains the implementation for CanBusInterface.
3 |
4 | TODO: complete implementation
5 | """
6 | import logging
7 | import threading
8 | import serial
9 |
10 |
11 | from interfaces.base import BaseInterface
12 |
13 |
14 | LOGGER = logging.getLogger(__name__)
15 |
16 |
17 | class CanBusInterface(BaseInterface):
18 | """The CAN (Controller Area Network) bus interface definition for communication with vehicles."""
19 |
20 | __interface_name__ = 'canbus'
21 |
22 | def __init__(self, controller):
23 | """
24 | Initializes bi-directional communication with CAN bus adapter via USB
25 |
26 | Arguments
27 | ---------
28 | controller : controllers.base.BaseController
29 | the parent controller that instantiated this interface
30 |
31 | """
32 | super(CanBusInterface, self).__init__()
33 | self.controller = controller
34 | self.baudrate = self.get_setting('baudrate', int)
35 | self.handle = None
36 | self.port = self.get_setting('port')
37 | self.timeout = self.get_setting('timeout', int)
38 | self.thread = None
39 |
40 | def connect(self):
41 | """Connect to the vehicle via serial communication."""
42 | if not self.state == self.__states__.STATE_READY:
43 | LOGGER.error('error: interface is not STATE_READY')
44 | raise Exception('invalid interface state: %r', self.state)
45 |
46 | # initialize serial port connection
47 | try:
48 | self.state = self.__states__.STATE_CONNECTING
49 | self.handle = serial.Serial(self.port, self.baudrate, timeout=self.timeout)
50 | self.state = self.__states__.STATE_CONNECTED
51 | except serial.serialutil.SerialException:
52 | LOGGER.exception('failed to establish serial connection')
53 | return False
54 |
55 | # launch new thread for continuous processing of the bus
56 | LOGGER.info('creating thread for %s interface...', self.__interface_name__)
57 | self.thread = threading.Thread(target=self.consume_bus)
58 | self.thread.daemon = True
59 | self.thread.start()
60 |
61 | def consume_bus(self):
62 | """Starts an infinite loop on the thread that will continue to read from the bus."""
63 | read_buffer_length = self.get_setting('read_buffer_length', int)
64 |
65 | while self.handle:
66 | data = self.handle.read(read_buffer_length)
67 | if len(data) > 0:
68 | self.receive(data)
69 |
70 | def disconnect(self):
71 | """Closes serial connection and resets the handle."""
72 | try:
73 | LOGGER.info('destroying %s interface...', self.__interface_name__)
74 | self.state = self.__states__.STATE_DISCONNECTING
75 | if self.handle and hasattr(self.handle, 'close'):
76 | self.handle.close()
77 | except Exception as exception:
78 | LOGGER.exception('exception during %s disconnect - %r', self.__interface_name__, exception)
79 | finally:
80 | self.state = self.__states__.STATE_READY
81 | self.handle = None
82 | self.thread = None
83 |
84 | def receive(self, data):
85 | """
86 | Processes bytes received from the interface.
87 |
88 | Arguments
89 | ---------
90 | data : basestring
91 | the bytes retrieved from the bus, encoded as a basestring
92 |
93 | """
94 | LOGGER.info('bus dump: hex: %r', data.encode('hex'))
95 |
96 | # TODO process packets
97 | packets = []
98 |
99 | # invoke bound method (if set)
100 | if self.receive_hook and hasattr(self.receive_hook, '__call__'):
101 | self.receive_hook(packets)
102 |
103 | def send(self, data):
104 | """
105 | Writes the provided hex packet(s) to the bus
106 |
107 | Parameters
108 | ----------
109 | data : basestring
110 | the data to be sent via this interface
111 |
112 | """
113 | if self.state != self.__states__.STATE_CONNECTED:
114 | LOGGER.error('error: send() was called but state is not connected')
115 | return False
116 |
117 | if not self.handle or not hasattr(self.handle, 'write'):
118 | LOGGER.error('cannot write to %s interface', self.__interface_name__)
119 | self.reconnect()
120 | return
121 |
122 | self.handle.write(data)
123 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | 
5 | 
6 | [](https://github.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller/issues)
7 | 
8 | 
9 |
10 |
11 | "Appify" your E46 BMW with a Raspberry Pi and your Smartphone! This repository contains the IBUS controller, as well as the Android application which supports smart phones and wearables. This is to be used with the IBUS USB interface which can be acquired from [Reslers.de](http://www.reslers.de/IBUS/), or from Amazon/eBay.
12 |
13 | Blog Series on Medium:
14 | - [Connecting a BMW to the Internet: Part One](https://medium.com/design-and-tech-co/connecting-a-bmw-to-the-internet-part-one-fbe18a54121d)
15 | - [Connecting a BMW to the Internet: Part Two](https://medium.com/@trentseed/connecting-a-bmw-to-the-internet-part-two-1ee2ea44d4a2)
16 | - [Connecting a BMW to the Internet: Part Three](https://medium.com/@trentseed/connecting-a-bmw-to-the-internet-part-three-89fb322b1dcb)
17 |
18 | ## Overview
19 |
20 | There are three main components of the solution:
21 | * Android Tablet - Primary front-end device for interacting with the car
22 | * Raspberry Pi - Mini-PC that handles communication between the IBUS adapter and the Android device
23 | * IBUS USB Adapter - USB adapter that provides USB/UART interface from physical wire in car
24 |
25 | 
26 |
27 | There is also an Android Wear (4.4W) component that allows you to control your vehicle from your smart watch!
28 | * Android Wear Device - Secondary device that provides quick interactions with the car
29 |
30 | 
31 |
32 | ## Pre-Requisites
33 | ### 1. Car Installation
34 | * Remove OEM Navigation Head Unit from BMW
35 | * Tap the 12V (red) wire, the GND (black) wire, and the IBUS (red/white/yellow) wire
36 | * Connect IBUS USB adapter to BMW (using 12V, GND, IBUS)
37 | * Install USB 12V - 5V adapter (using 12V, GND)
38 | * Install Raspberry Pi in the dash (and power w/ 5V from adapter)
39 | * Connect IBUS USB adapter to Raspberry Pi via USB cable
40 | * Connect Ethernet cable to Raspberry Pi (and wire to glove box)
41 |
42 | ### 2. Raspberry Pi 3
43 | * Install Raspbian (or another OS that supports python)
44 | * One tutorial [can be found here](http://computers.tutsplus.com/articles/how-to-flash-an-sd-card-for-raspberry-pi--mac-53600)
45 | * Pair Android device with Raspberry Pi via Bluetooth
46 |
47 | #### 2a. Running with Docker
48 | * Install docker and docker-compose
49 | * `curl -fsSL get.docker.com -o get-docker.sh && sh get-docker.sh`
50 | * `pip install docker-compose`
51 | * Download project and run with docker-compose
52 | * `git clone https://github.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller controller`
53 | * `cd controller/connected_car_controller/`
54 | * `docker-compose up --build -d`
55 |
56 | #### 2b. Running without Docker
57 | * Install packages
58 | * `apt-get install build-essential bluez bluez-tools libbluetooth-dev`
59 | * Download project
60 | * `git clone https://github.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller controller`
61 | * `cd controller/connected_car_controller/`
62 | * Install python modules:
63 | * `pip install -r requirements.txt`
64 | * Run the project: `python3 start_controller.py bmw-e46`
65 |
66 | ### 3. Android Mobile / Tablet
67 | * Update `BluetoothInterface.remoteBluetoothAddress` of 'connected_car_app/common' with RPi Bluetooth address
68 | * Build Android project `connected_car_app/` via Android Studio
69 | * Install the applicaton
70 | * You can build and run in Android Studio by connecting your devices
71 | * You can also install by transfering the built APKs
72 | * `connected_car_app/app/build/outputs/apk/debug/app-debug.apk`
73 | * `connected_car_app/ibuswear/build/outputs/apk/debug/ibuswear-debug.apk`
74 |
75 | ## How To Get Started
76 | * Install the prerequisites above
77 | * Ensure Android device is paired with Raspberry Pi via Bluetooth
78 | * Run the controller as daemon: `docker-compose up -d`
79 | * Launch Connected Car App on your Android device or wearable
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/java/com/trentseed/bmw_rpi_ibus_controller/AdapterIBUS.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller;
2 |
3 | import java.text.SimpleDateFormat;
4 | import java.util.Calendar;
5 |
6 | import android.annotation.SuppressLint;
7 | import android.graphics.Color;
8 | import android.util.TypedValue;
9 | import android.view.Gravity;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.view.ViewGroup.LayoutParams;
13 | import android.widget.BaseAdapter;
14 | import android.widget.GridView;
15 | import android.widget.ImageView;
16 | import android.widget.LinearLayout;
17 | import android.widget.TextView;
18 |
19 | import com.trentseed.bmw_rpi_ibus_controller.common.BluetoothInterface;
20 | import com.trentseed.bmw_rpi_ibus_controller.common.IBUSPacket;
21 |
22 | /**
23 | * Adapter that is used to display IBUS activity
24 | * @author Trent Seed
25 | */
26 | public class AdapterIBUS extends BaseAdapter{
27 |
28 | private ActivityIBUS thisActivity;
29 |
30 | AdapterIBUS(ActivityIBUS thisActivity){
31 | //store context and determine enroll status
32 | this.thisActivity = thisActivity;
33 | }
34 |
35 | public int getCount() {
36 | return BluetoothInterface.mArrayListIBUSActivity.size();
37 | }
38 |
39 | public IBUSPacket getItem(int position) {
40 | return BluetoothInterface.mArrayListIBUSActivity.get(position);
41 | }
42 |
43 | public long getItemId(int position) {
44 | return BluetoothInterface.mArrayListIBUSActivity.get(position).hashCode();
45 | }
46 |
47 | @Override
48 | public void notifyDataSetChanged(){
49 | super.notifyDataSetChanged();
50 |
51 | if(this.getCount() == 0){
52 | thisActivity.tvNoActivity.setVisibility(View.VISIBLE);
53 | }else{
54 | thisActivity.tvNoActivity.setVisibility(View.GONE);
55 | }
56 | }
57 |
58 | public View getView(final int position, View convertView, ViewGroup parent) {
59 | // get this object
60 | IBUSPacket ibPacket = BluetoothInterface.mArrayListIBUSActivity.get(position);
61 |
62 | // create field view
63 | LinearLayout viewContainer = new LinearLayout(thisActivity);
64 | viewContainer.setOrientation(LinearLayout.HORIZONTAL);
65 | viewContainer.setGravity(Gravity.START);
66 | viewContainer.setPadding(40, 20, 20, 0);
67 |
68 | // packet image view
69 | float height = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80 ,thisActivity.getResources().getDisplayMetrics());
70 | ImageView ivPacket = new ImageView(thisActivity);
71 | ivPacket.setImageResource(R.drawable.packet);
72 | GridView.LayoutParams imgParams = new GridView.LayoutParams((int)height, (int)height);
73 | ivPacket.setLayoutParams(imgParams);
74 | ivPacket.setPadding(10, 10, 10, 10);
75 |
76 | // label
77 | TextView labelKey = new TextView(thisActivity);
78 | String labelText = "Source: " + ibPacket.source_id + " (" + ibPacket.getSourceName() + ") @ "
79 | + AdapterIBUS.getDate(Long.parseLong(ibPacket.timestamp), "hh:mm") + "\n"
80 | + "Destination: " + ibPacket.destination_id + " (" + ibPacket.getDestinationName() + ")\n"
81 | + "Datagram: " + ibPacket.raw;
82 | labelKey.setText(labelText);
83 | labelKey.setTextColor(Color.WHITE);
84 | GridView.LayoutParams params = new GridView.LayoutParams(LayoutParams.MATCH_PARENT, (int)height, 1);
85 | labelKey.setGravity(Gravity.CENTER_VERTICAL);
86 | labelKey.setPadding(40, 0, 0, 0);
87 | labelKey.setLayoutParams(params);
88 | labelKey.setTextSize(18.0f);
89 |
90 | // timestamp
91 | TextView tvTimestamp = new TextView(thisActivity);
92 | String labelTimestamp = ibPacket.timestamp;
93 | tvTimestamp.setText(labelTimestamp);
94 | tvTimestamp.setTextColor(Color.WHITE);
95 | GridView.LayoutParams paramsTimestamp = new GridView.LayoutParams(LayoutParams.MATCH_PARENT, (int)height, 1);
96 | tvTimestamp.setGravity(Gravity.CENTER_VERTICAL);
97 | tvTimestamp.setPadding(40, 0, 0, 0);
98 | tvTimestamp.setLayoutParams(paramsTimestamp);
99 | tvTimestamp.setTextSize(20.0f);
100 |
101 | // add sub-views to container view
102 | viewContainer.addView(ivPacket);
103 | viewContainer.addView(labelKey);
104 | //viewContainer.addView(tvTimestamp);
105 | return viewContainer;
106 | }
107 |
108 | /**
109 | * Return date in specified format.
110 | * @param milliSeconds Date in milliseconds
111 | * @param dateFormat Date format
112 | * @return String representing date in specified format
113 | */
114 | @SuppressLint("SimpleDateFormat")
115 | private static String getDate(long milliSeconds, String dateFormat)
116 | {
117 | // Create a DateFormatter object for displaying date in specified format.
118 | SimpleDateFormat formatter = new SimpleDateFormat(dateFormat);
119 |
120 | // Create a calendar object that will convert the date and time value in milliseconds to date.
121 | Calendar calendar = Calendar.getInstance();
122 | calendar.setTimeInMillis(milliSeconds);
123 | return formatter.format(calendar.getTime());
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/connected_car_app/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/layout/activity_radio.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
18 |
19 |
26 |
27 |
30 |
31 |
39 |
40 |
50 |
51 |
52 |
53 |
56 |
57 |
64 |
65 |
70 |
71 |
76 |
77 |
82 |
83 |
88 |
89 |
90 |
97 |
98 |
103 |
104 |
109 |
110 |
115 |
116 |
121 |
122 |
127 |
128 |
133 |
134 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/connected_car_controller/interfaces/bt.py:
--------------------------------------------------------------------------------
1 | """
2 | This module contains the implementation for BluetoothInterface.
3 | """
4 | import json
5 | import threading
6 | import subprocess
7 | import logging
8 |
9 |
10 | from bluetooth import advertise_service
11 | from bluetooth import BluetoothSocket
12 | from bluetooth import RFCOMM
13 | from bluetooth import PORT_ANY
14 | from bluetooth import SERIAL_PORT_CLASS
15 | from bluetooth import SERIAL_PORT_PROFILE
16 |
17 |
18 | from interfaces.base import BaseInterface
19 |
20 |
21 | LOGGER = logging.getLogger(__name__)
22 |
23 |
24 | class BluetoothInterface(BaseInterface):
25 | """The Bluetooth interface definition for communication with wireless client devices."""
26 |
27 | __interface_name__ = 'bluetooth'
28 |
29 | def __init__(self, controller):
30 | """
31 | Initializes a bluetooth service that may be consumed by a remote client.
32 |
33 | Arguments
34 | ---------
35 | controller : controllers.base.BaseController
36 | the parent controller that is instantiated this interface
37 |
38 | """
39 | super(BluetoothInterface, self).__init__()
40 | self.controller = controller
41 | self.client_sock = None
42 | self.client_info = None
43 | self.rfcomm_channel = None
44 | self.service_name = self.get_setting('service_name')
45 | self.service_uuid = self.get_setting('service_uuid')
46 | self.server_sock = None
47 | self.thread = None
48 |
49 | def connect(self):
50 | """Creates a new thread that listens for an incoming bluetooth RFCOMM connection."""
51 | LOGGER.info('creating thread for bluetooth interface...')
52 | self.thread = threading.Thread(target=self.listen_for_rfcomm_connection)
53 | self.thread.daemon = True
54 | self.thread.start()
55 |
56 | def listen_for_rfcomm_connection(self):
57 | """
58 | Starts bluetooth interfaces
59 | """
60 | # prepare bluetooth server
61 | self.server_sock = BluetoothSocket(RFCOMM)
62 | self.server_sock.bind(("", PORT_ANY))
63 | self.server_sock.listen(1)
64 | self.rfcomm_channel = self.server_sock.getsockname()[1]
65 |
66 | # start listening for incoming connections
67 | try:
68 | advertise_service(
69 | sock=self.server_sock,
70 | name=self.service_name,
71 | service_id=self.service_uuid,
72 | service_classes=[self.service_uuid, SERIAL_PORT_CLASS],
73 | profiles=[SERIAL_PORT_PROFILE]
74 | )
75 | except:
76 | LOGGER.exception("[ERROR] failed to advertise service")
77 | return
78 |
79 | LOGGER.info('waiting for connection on RFCOMM channel %d', self.rfcomm_channel)
80 |
81 | # accept received connection
82 | self.client_sock, self.client_info = self.server_sock.accept()
83 | self.state = self.__states__.STATE_CONNECTED
84 | LOGGER.info('accepted connection from %r', self.client_info)
85 |
86 | # start listening for data
87 | self.consume_bus()
88 |
89 | def disconnect(self):
90 | """
91 | Closes Bluetooth connection and resets handle
92 | """
93 | LOGGER.info('destroying bluetooth interface...')
94 | self.state = self.__states__.STATE_DISCONNECTING
95 | if self.client_sock and hasattr(self.client_sock, 'close'):
96 | self.client_sock.close()
97 | self.client_sock = None
98 | if self.server_sock and hasattr(self.server_sock, 'close'):
99 | self.server_sock.close()
100 | self.server_sock = None
101 |
102 | # reset the bluetooth interface
103 | self.thread = None
104 | self.perform_hci0_reset()
105 | self.state = self.__states__.STATE_READY
106 |
107 | @staticmethod
108 | def perform_hci0_reset():
109 | """Resets the bluetooth hci0 device via hciconfig command line interface."""
110 | try:
111 | LOGGER.info('performing hci0 down/up...')
112 | subprocess.Popen('hciconfig hci0 down', shell=True).communicate()
113 | subprocess.Popen('hciconfig hci0 up', shell=True).communicate()
114 | LOGGER.info('hci0 down/up has completed')
115 | except Exception as exception:
116 | LOGGER.exception("Failed to restart hci0 - %r", exception)
117 |
118 | def receive(self, data):
119 | """
120 | Processes received data from Bluetooth socket
121 |
122 | Arguments
123 | ---------
124 | data : basestring
125 | the data received from the bluetooth connection
126 |
127 | """
128 | try:
129 | packet = json.loads(data.decode('utf-8'))
130 | LOGGER.info('received packet via bluetooth: %r', packet['data'])
131 |
132 | # invoke bound method (if set)
133 | if self.receive_hook and hasattr(self.receive_hook, '__call__'):
134 | self.receive_hook(packet['data'])
135 |
136 | except Exception as exception:
137 | LOGGER.exception('error: %r', exception)
138 |
139 | def send(self, data):
140 | """
141 | Sends data via Bluetooth socket connection
142 |
143 | Arguments
144 | ---------
145 | data : basestring
146 | the data to be sent via this interface
147 |
148 | """
149 | if self.state != self.__states__.STATE_CONNECTED:
150 | LOGGER.error('error: send() was called but state is not connected')
151 | return False
152 |
153 | try:
154 | LOGGER.info('sending IBUSPacket(s)...')
155 | packets = []
156 | for packet in data:
157 | packets.append(packet.as_serializable_dict())
158 |
159 | # encapsulate ibus packets and send
160 | data = {"data": json.dumps(packets)} # TODO : is an inner json.dumps necessary?
161 | LOGGER.info(data)
162 | self.client_sock.send(json.dumps(data))
163 | except Exception:
164 | # socket was closed, graceful restart
165 | LOGGER.exception('bluetooth send exception')
166 | self.reconnect()
167 |
168 | def consume_bus(self):
169 | """
170 | Start listening for incoming data
171 | """
172 | try:
173 | LOGGER.info('starting to listen for bluetooth data...')
174 | read_buffer_length = self.get_setting('read_buffer_length', int)
175 |
176 | while self.client_sock:
177 | data = self.client_sock.recv(read_buffer_length)
178 | if len(data) > 0:
179 | self.receive(data)
180 |
181 | except Exception as exception:
182 | LOGGER.exception('android device was disconnected - %r', exception)
183 | self.reconnect()
184 |
--------------------------------------------------------------------------------
/connected_car_app/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
19 |
20 |
32 |
33 |
41 |
42 |
49 |
50 |
55 |
56 |
63 |
64 |
65 |
70 |
71 |
78 |
79 |
80 |
81 |
82 |
83 |
89 |
90 |
97 |
98 |
104 |
105 |
112 |
113 |
114 |
115 |
119 |
120 |
130 |
131 |
132 |
133 |
134 |
135 |
142 |
143 |
148 |
149 |
156 |
157 |
158 |
163 |
164 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
--------------------------------------------------------------------------------
/connected_car_app/common/src/main/java/com/trentseed/bmw_rpi_ibus_controller/common/IBUSWrapper.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.common;
2 |
3 | import com.google.gson.Gson;
4 |
5 | import android.app.Activity;
6 | import android.content.Intent;
7 | import android.util.Log;
8 | import android.view.KeyEvent;
9 |
10 | /**
11 | * Navigation class handles core communication with Raspberry Pi.
12 | * @author Trent
13 | *
14 | */
15 | public class IBUSWrapper {
16 |
17 | // special packets
18 | public static final String KEY_INSERTED = "4405bf7404028c";
19 | public static final String KEY_REMOTE_LOCK = "3f05000c340103";
20 | public static final String KEY_REMOTE_UNLOCK = "3f05000c030134";
21 | public static final String RADIO_POLLING_CD_REQUEST = "6803180172";
22 | public static final String RADIO_POLLING_CD_RESPONSE = "180468020076";
23 |
24 | /**
25 | * Processes a received IBUSPacket (this is extracted from original ControllerMessage)
26 | * BMW --(IBUS USB)--> RaspberryPi --(Bluetooth)--> Android
27 | */
28 | public static void processPacket(IBUSPacket ibPacket){
29 | // add packet to local buffer (IBUS activity displays buffer)
30 | int max_buffer = 20;
31 | BluetoothInterface.mArrayListIBUSActivity.add(0, ibPacket);
32 | if(BluetoothInterface.mArrayListIBUSActivity.size() > max_buffer){
33 | BluetoothInterface.mArrayListIBUSActivity.remove(
34 | BluetoothInterface.mArrayListIBUSActivity.size()-1
35 | );
36 | }
37 |
38 | // check for special packets
39 | if(ibPacket.raw.equals(KEY_INSERTED)){
40 | // special packet - key inserted
41 | Log.d("BMW", "Key was inserted now...");
42 | }else if(ibPacket.raw.equals(KEY_REMOTE_LOCK)){
43 | // special packet - car locked
44 | Log.d("BMW", "Car was locked now...");
45 | }else if(ibPacket.raw.equals(KEY_REMOTE_UNLOCK)){
46 | // special packet - car unlocked
47 | Log.d("BMW", "Car was unlocked now...");
48 | }else if(ibPacket.raw.equals(RADIO_POLLING_CD_REQUEST)){
49 | // special packet - radio polling CD changer
50 | Log.d("BMW", "Radio polling CD changer now...");
51 | sendCDChangerPollResponse();
52 | }
53 | }
54 |
55 | /**
56 | * Sends a ControllerMessage object to the RaspberryPi over Bluetooth.
57 | * @param data - the ControllerMessage instance to send
58 | */
59 | private static void sendMessage(ControllerMessage data){
60 | try {
61 | if(BluetoothInterface.isConnected()){
62 | Log.d("BMW", "writing bytes to output stream...");
63 | byte[] bytes = new Gson().toJson(data).getBytes();
64 | BluetoothInterface.mBluetoothOutputStream.write(bytes);
65 | }else{
66 | Log.d("BMW", "cannot write bytes, not connected.");
67 | }
68 | } catch (Exception e){
69 | e.printStackTrace();
70 | }
71 | }
72 |
73 | /**
74 | * Advance to next audio track (i.e. Google Music). This can be called when
75 | * detecting a particular packet, such as the steering wheel audio controls.
76 | */
77 | public static void nextTrack(Activity thisActivity){
78 | Log.d("BMW", "ATTEMPTING TO GO TO NEXT TRACK");
79 | Intent i = new Intent(Intent.ACTION_MEDIA_BUTTON);
80 | synchronized (thisActivity) {
81 | i.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT));
82 | thisActivity.sendOrderedBroadcast(i, null);
83 |
84 | i.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT));
85 | thisActivity.sendOrderedBroadcast(i, null);
86 | }
87 | }
88 |
89 | public static void toggleMode(Activity thisActivity){
90 | ControllerMessage msg = new ControllerMessage();
91 | msg.data = "f004684823f7" + "f0046848a377" + "680b3ba562014120464d412095";
92 | IBUSWrapper.sendMessage(msg);
93 | }
94 |
95 | public static void volumeUp(){
96 | ControllerMessage msg = new ControllerMessage();
97 | msg.data = "50046832111f";
98 | IBUSWrapper.sendMessage(msg);
99 | }
100 |
101 | public static void volumeDown(){
102 | ControllerMessage msg = new ControllerMessage();
103 | msg.data = "50046832101e";
104 | IBUSWrapper.sendMessage(msg);
105 | }
106 |
107 | public static void turnInteriorLightsOff(){
108 | ControllerMessage msg = new ControllerMessage();
109 | msg.data = "3f05000c010136";
110 | IBUSWrapper.sendMessage(msg);
111 | }
112 |
113 | public static void turnInteriorLightsOn(){
114 | ControllerMessage msg = new ControllerMessage();
115 | msg.data = "3f05000c600157";
116 | IBUSWrapper.sendMessage(msg);
117 | }
118 |
119 | public static void openTrunk(){
120 | ControllerMessage msg = new ControllerMessage();
121 | msg.data = "3f05000c020135";
122 | IBUSWrapper.sendMessage(msg);
123 | }
124 |
125 | public static void pressAccept(){
126 | ControllerMessage msg = new ControllerMessage();
127 | msg.data = "f0043b480582" + "f0043b488502";
128 | IBUSWrapper.sendMessage(msg);
129 | }
130 |
131 | public static void toggleHazards(){
132 | ControllerMessage msg = new ControllerMessage();
133 | msg.data = "0004bf7602cf";
134 | IBUSWrapper.sendMessage(msg);
135 | }
136 |
137 | public static void toggleRadioPower(){
138 | ControllerMessage msg = new ControllerMessage();
139 | msg.data = "f004684806d2";
140 | IBUSWrapper.sendMessage(msg);
141 | }
142 |
143 | public static void windowDriverFront(boolean down){
144 | ControllerMessage msg = new ControllerMessage();
145 | if(down) msg.data = "3F05000C520165";
146 | else msg.data = "3F05000C530164";
147 | IBUSWrapper.sendMessage(msg);
148 | }
149 |
150 | public static void windowDriverRear(boolean down){
151 | ControllerMessage msg = new ControllerMessage();
152 | if(down) msg.data = "3F05000C410176";
153 | else msg.data = "3F05000C420175";
154 | IBUSWrapper.sendMessage(msg);
155 | }
156 |
157 | public static void windowPassengerFront(boolean down){
158 | ControllerMessage msg = new ControllerMessage();
159 | if(down) msg.data = "3F05000C540163";
160 | else msg.data = "3F05000C550162";
161 | IBUSWrapper.sendMessage(msg);
162 | }
163 |
164 | public static void windowPassengerRear(boolean down){
165 | ControllerMessage msg = new ControllerMessage();
166 | if(down) msg.data = "3F05000C440173";
167 | else msg.data = "3F05000C430174";
168 | IBUSWrapper.sendMessage(msg);
169 | }
170 |
171 | public static void moveDriverSeat(boolean forward){
172 | ControllerMessage msg = new ControllerMessage();
173 | if(forward) msg.data = "3F06720C01010047";
174 | else msg.data = "3F06720C01020044";
175 | IBUSWrapper.sendMessage(msg);
176 | }
177 |
178 | public static void moveDriverSeatUpperPortion(boolean forward){
179 | ControllerMessage msg = new ControllerMessage();
180 | if(forward) msg.data = "3f06720c01400006";
181 | else msg.data = "3f06720c018000c6";
182 | IBUSWrapper.sendMessage(msg);
183 | }
184 |
185 | public static void lockCar(){
186 | ControllerMessage msg = new ControllerMessage();
187 | msg.data = "3F05000C340103";
188 | IBUSWrapper.sendMessage(msg);
189 | }
190 |
191 | public static void unlockCar(){
192 | ControllerMessage msg = new ControllerMessage();
193 | msg.data = "3F05000C030134";
194 | IBUSWrapper.sendMessage(msg);
195 | }
196 |
197 | public static void toggleSunroof(boolean open){
198 | ControllerMessage msg = new ControllerMessage();
199 | if(open) msg.data = "3F05000C7E0149";
200 | else msg.data = "3F05000C7F0148";
201 | IBUSWrapper.sendMessage(msg);
202 | }
203 |
204 | private static void sendCDChangerPollResponse(){
205 | ControllerMessage msg = new ControllerMessage();
206 | msg.data = RADIO_POLLING_CD_RESPONSE;
207 | IBUSWrapper.sendMessage(msg);
208 | }
209 |
210 | }
--------------------------------------------------------------------------------
/connected_car_app/ibuswear/src/main/java/com/trentseed/bmw_rpi_ibus_controller/wear/IBUSGridPagerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.wear;
2 |
3 | import android.app.Fragment;
4 | import android.app.FragmentManager;
5 | import android.content.Context;
6 | import android.graphics.Color;
7 | import android.graphics.drawable.ColorDrawable;
8 | import android.graphics.drawable.Drawable;
9 | import android.support.wearable.view.FragmentGridPagerAdapter;
10 |
11 | @SuppressWarnings("WeakerAccess")
12 | public class IBUSGridPagerAdapter extends FragmentGridPagerAdapter {
13 |
14 | /* Identifiers for each fragment view of the app. */
15 | public static int PAGE_WELCOME = 10;
16 | public static int PAGE_ABOUT = 11;
17 | public static int PAGE_VOICE_COMMAND = 12;
18 | public static int PAGE_EXIT = 13;
19 | public static int PAGE_DRIVER_LANDING = 20;
20 | public static int PAGE_DRIVER_UP = 21;
21 | public static int PAGE_DRIVER_DOWN = 22;
22 | public static int PAGE_PASSENGER_LANDING = 30;
23 | public static int PAGE_PASSENGER_UP = 31;
24 | public static int PAGE_PASSENGER_DOWN = 32;
25 | public static int PAGE_REAR_LEFT_LANDING = 40;
26 | public static int PAGE_REAR_LEFT_UP = 41;
27 | public static int PAGE_REAR_LEFT_DOWN = 42;
28 | public static int PAGE_REAR_RIGHT_LANDING = 50;
29 | public static int PAGE_REAR_RIGHT_UP = 51;
30 | public static int PAGE_REAR_RIGHT_DOWN = 52;
31 | public static int PAGE_LOCKS_LANDING = 60;
32 | public static int PAGE_LOCKS_LOCK = 61;
33 | public static int PAGE_LOCKS_UNLOCK = 62;
34 | public static int PAGE_VOLUME_LANDING = 70;
35 | public static int PAGE_VOLUME_UP = 71;
36 | public static int PAGE_VOLUME_DOWN = 72;
37 | public static int PAGE_SUNROOF_LANDING = 80;
38 | public static int PAGE_SUNROOF_OPEN = 81;
39 | public static int PAGE_SUNROOF_CLOSE = 82;
40 | public static int PAGE_DRIVER_SEAT_LANDING = 90;
41 | public static int PAGE_DRIVER_SEAT_FORWARD = 91;
42 | public static int PAGE_DRIVER_SEAT_BACK = 92;
43 |
44 | public final Context mContext;
45 |
46 | public IBUSGridPagerAdapter(Context ctx, FragmentManager fm) {
47 | super(fm);
48 | mContext = ctx;
49 | }
50 |
51 | // A simple container for static data in each page
52 | private static class Page {
53 | int page_id;
54 |
55 | private Page(int page_id){
56 | this.page_id = page_id;
57 | }
58 | }
59 |
60 | // Create a static set of pages in a 2D array
61 | private final Page[][] PAGES = {
62 | {
63 | new Page(PAGE_WELCOME),
64 | new Page(PAGE_VOICE_COMMAND),
65 | new Page(PAGE_EXIT),
66 | new Page(PAGE_ABOUT)
67 | },
68 | {
69 | new Page(PAGE_LOCKS_LANDING),
70 | new Page(PAGE_LOCKS_LOCK),
71 | new Page(PAGE_LOCKS_UNLOCK)
72 | },
73 | {
74 | new Page(PAGE_VOLUME_LANDING),
75 | new Page(PAGE_VOLUME_UP),
76 | new Page(PAGE_VOLUME_DOWN)
77 | },
78 | {
79 | new Page(PAGE_SUNROOF_LANDING),
80 | new Page(PAGE_SUNROOF_OPEN),
81 | new Page(PAGE_SUNROOF_CLOSE)
82 | },
83 | {
84 | new Page(PAGE_DRIVER_SEAT_LANDING),
85 | new Page(PAGE_DRIVER_SEAT_FORWARD),
86 | new Page(PAGE_DRIVER_SEAT_BACK)
87 | },
88 | {
89 | new Page(PAGE_DRIVER_LANDING),
90 | new Page(PAGE_DRIVER_UP),
91 | new Page(PAGE_DRIVER_DOWN)
92 | },
93 | {
94 | new Page(PAGE_PASSENGER_LANDING),
95 | new Page(PAGE_PASSENGER_UP),
96 | new Page(PAGE_PASSENGER_DOWN)
97 | },
98 | {
99 | new Page(PAGE_REAR_LEFT_LANDING),
100 | new Page(PAGE_REAR_LEFT_UP),
101 | new Page(PAGE_REAR_LEFT_DOWN)
102 | },
103 | {
104 | new Page(PAGE_REAR_RIGHT_LANDING),
105 | new Page(PAGE_REAR_RIGHT_UP),
106 | new Page(PAGE_REAR_RIGHT_DOWN)
107 | }
108 | };
109 |
110 | // Obtain the UI fragment at the specified position
111 | @Override
112 | public Fragment getFragment(int row, int col) {
113 | Page page = PAGES[row][col];
114 |
115 | Fragment newFrag;
116 |
117 | if (page.page_id == PAGE_WELCOME){
118 | ActivityMain.parent_page_id = PAGE_WELCOME;
119 | newFrag = new FragmentWelcome();
120 | }else if (page.page_id == PAGE_ABOUT){
121 | newFrag = new FragmentAbout();
122 | }else if (page.page_id == PAGE_VOICE_COMMAND){
123 | newFrag = new FragmentVoiceCommand();
124 | }else if (page.page_id == PAGE_EXIT){
125 | newFrag = new FragmentExit();
126 | }else if (page.page_id == PAGE_DRIVER_LANDING){
127 | ActivityMain.parent_page_id = PAGE_DRIVER_LANDING;
128 | newFrag = new FragmentWindowFrontLeft();
129 | }else if (page.page_id == PAGE_DRIVER_UP){
130 | newFrag = new FragmentWindowFrontLeftActionUp();
131 | }else if (page.page_id == PAGE_DRIVER_DOWN){
132 | newFrag = new FragmentWindowFrontLeftActionDown();
133 | }else if (page.page_id == PAGE_PASSENGER_LANDING){
134 | ActivityMain.parent_page_id = PAGE_PASSENGER_LANDING;
135 | newFrag = new FragmentWindowFrontRight();
136 | }else if (page.page_id == PAGE_PASSENGER_UP){
137 | newFrag = new FragmentWindowFrontRightActionUp();
138 | }else if (page.page_id == PAGE_PASSENGER_DOWN){
139 | newFrag = new FragmentWindowFrontRightActionDown();
140 | }else if (page.page_id == PAGE_REAR_LEFT_LANDING){
141 | ActivityMain.parent_page_id = PAGE_REAR_LEFT_LANDING;
142 | newFrag = new FragmentWindowBackLeft();
143 | }else if (page.page_id == PAGE_REAR_LEFT_UP){
144 | newFrag = new FragmentWindowBackLeftActionUp();
145 | }else if (page.page_id == PAGE_REAR_LEFT_DOWN){
146 | newFrag = new FragmentWindowBackLeftActionDown();
147 | }else if (page.page_id == PAGE_REAR_RIGHT_LANDING){
148 | ActivityMain.parent_page_id = PAGE_REAR_RIGHT_LANDING;
149 | newFrag = new FragmentWindowBackRight();
150 | }else if (page.page_id == PAGE_REAR_RIGHT_UP){
151 | newFrag = new FragmentWindowBackRightActionUp();
152 | }else if (page.page_id == PAGE_REAR_RIGHT_DOWN){
153 | newFrag = new FragmentWindowBackRightActionDown();
154 | }else if (page.page_id == PAGE_LOCKS_LANDING){
155 | ActivityMain.parent_page_id = PAGE_LOCKS_LANDING;
156 | newFrag = new FragmentKeys();
157 | }else if (page.page_id == PAGE_LOCKS_LOCK){
158 | newFrag = new FragmentKeysLock();
159 | }else if (page.page_id == PAGE_LOCKS_UNLOCK){
160 | newFrag = new FragmentKeysUnlock();
161 | }else if (page.page_id == PAGE_VOLUME_LANDING){
162 | ActivityMain.parent_page_id = PAGE_VOLUME_LANDING;
163 | newFrag = new FragmentVolume();
164 | }else if (page.page_id == PAGE_VOLUME_UP){
165 | newFrag = new FragmentVolumeUp();
166 | }else if (page.page_id == PAGE_VOLUME_DOWN){
167 | newFrag = new FragmentVolumeDown();
168 | }else if (page.page_id == PAGE_SUNROOF_LANDING){
169 | ActivityMain.parent_page_id = PAGE_SUNROOF_LANDING;
170 | newFrag = new FragmentSunroof();
171 | }else if (page.page_id == PAGE_SUNROOF_OPEN){
172 | newFrag = new FragmentSunroofOpen();
173 | }else if (page.page_id == PAGE_SUNROOF_CLOSE){
174 | newFrag = new FragmentSunroofClose();
175 | }else if (page.page_id == PAGE_DRIVER_SEAT_LANDING){
176 | ActivityMain.parent_page_id = PAGE_DRIVER_SEAT_LANDING;
177 | newFrag = new FragmentDriverSeat();
178 | }else if (page.page_id == PAGE_DRIVER_SEAT_FORWARD){
179 | newFrag = new FragmentDriverSeatForward();
180 | }else if (page.page_id == PAGE_DRIVER_SEAT_BACK){
181 | newFrag = new FragmentDriverSeatBack();
182 | }else{
183 | ActivityMain.parent_page_id = PAGE_WELCOME;
184 | newFrag = new FragmentWelcome();
185 | }
186 |
187 | return newFrag;
188 | }
189 |
190 | // Obtain the background image for the page at the specified position
191 | @Override
192 | public Drawable getBackgroundForPage(int row, int column) {
193 | return new ColorDrawable(Color.TRANSPARENT);
194 | }
195 |
196 | // Obtain the number of pages (vertical)
197 | @Override
198 | public int getRowCount() {
199 | return PAGES.length;
200 | }
201 |
202 | // Obtain the number of pages (horizontal)
203 | @Override
204 | public int getColumnCount(int rowNum) {
205 | return PAGES[rowNum].length;
206 | }
207 |
208 | }
209 |
--------------------------------------------------------------------------------
/connected_car_app/common/src/main/java/com/trentseed/bmw_rpi_ibus_controller/common/VoiceCommand.java:
--------------------------------------------------------------------------------
1 | package com.trentseed.bmw_rpi_ibus_controller.common;
2 |
3 | import android.util.Log;
4 |
5 | public class VoiceCommand {
6 |
7 | /** cache the last command **/
8 | private static String LAST_COMMAND = "";
9 |
10 | /**
11 | * Process the spoken text input provided by the user.
12 | * TODO: Improve algorithm - this is an initial proof-of-concept!
13 | * @param spokenText
14 | */
15 | public static String processSpokenText(String spokenText){
16 | String userOutput = "Sorry, I did not catch that.";
17 | Log.d("BMW", "SPOKEN TEXT: " + spokenText);
18 |
19 | // determine action from spoken text
20 | if(spokenText.contains("debug")) {
21 | userOutput = spokenText.replace("debug ", "");
22 | }else if(spokenText.contains("again") && !LAST_COMMAND.contains("again")){
23 | processSpokenText(LAST_COMMAND);
24 | }else if(spokenText.contains("all windows") || spokenText.contains("every window")
25 | || spokenText.contains("each window")){
26 | if(spokenText.contains("down")){
27 | IBUSWrapper.windowDriverFront(true);
28 | IBUSWrapper.windowDriverRear(true);
29 | IBUSWrapper.windowPassengerFront(true);
30 | IBUSWrapper.windowPassengerRear(true);
31 | userOutput = "Rolling down all windows!";
32 | }else{
33 | IBUSWrapper.windowDriverFront(false);
34 | IBUSWrapper.windowDriverRear(false);
35 | IBUSWrapper.windowPassengerFront(false);
36 | IBUSWrapper.windowPassengerRear(false);
37 | userOutput = "Rolling up all windows!";
38 | }
39 | }else if(spokenText.contains("front windows")){
40 | if(spokenText.contains("down")){
41 | IBUSWrapper.windowDriverFront(true);
42 | IBUSWrapper.windowPassengerFront(true);
43 | userOutput = "Rolling down the front windows!";
44 | }else{
45 | IBUSWrapper.windowDriverFront(false);
46 | IBUSWrapper.windowPassengerFront(false);
47 | userOutput = "Rolling up the front windows!";
48 | }
49 | }else if(spokenText.contains("back windows") || spokenText.contains("rear windows")){
50 | if(spokenText.contains("down")){
51 | IBUSWrapper.windowDriverRear(true);
52 | IBUSWrapper.windowPassengerRear(true);
53 | userOutput = "Rolling down the rear windows!";
54 | }else{
55 | IBUSWrapper.windowDriverRear(false);
56 | IBUSWrapper.windowPassengerRear(false);
57 | userOutput = "Rolling up the rear windows!";
58 | }
59 | }else if(spokenText.contains("window")){
60 | if(spokenText.contains("driver") || spokenText.contains("drivers")){
61 | if(spokenText.contains("rear") || spokenText.contains("back")){
62 | if(spokenText.contains("down")){
63 | IBUSWrapper.windowDriverRear(true);
64 | userOutput = "Rolling down the rear driver window!";
65 | }else{
66 | IBUSWrapper.windowDriverRear(false);
67 | userOutput = "Rolling up the rear driver window!";
68 | }
69 | }else{
70 | if(spokenText.contains("down")){
71 | IBUSWrapper.windowDriverFront(true);
72 | userOutput = "Rolling down the driver window!";
73 | }else{
74 | IBUSWrapper.windowDriverFront(false);
75 | userOutput = "Rolling up the driver window!";
76 | }
77 | }
78 | }else if(spokenText.contains("passenger") || spokenText.contains("passengers")){
79 | if(spokenText.contains("rear") || spokenText.contains("back")){
80 | if(spokenText.contains("down")) {
81 | IBUSWrapper.windowPassengerRear(true);
82 | userOutput = "Rolling down the rear passenger window!";
83 | }else{
84 | IBUSWrapper.windowPassengerRear(false);
85 | userOutput = "Rolling up the rear passenger window!";
86 | }
87 | }else{
88 | if(spokenText.contains("down")) {
89 | IBUSWrapper.windowPassengerFront(true);
90 | userOutput = "Rolling down the passenger window!";
91 | }else{
92 | IBUSWrapper.windowPassengerFront(false);
93 | userOutput = "Rolling up the passenger window!";
94 | }
95 | }
96 | }
97 | }else if(spokenText.contains("lights")){
98 | if(spokenText.contains("interior")){
99 | if(spokenText.contains("off")){
100 | IBUSWrapper.turnInteriorLightsOff();
101 | userOutput = "Turning off interior lights!";
102 | }else if(spokenText.contains("on")){
103 | IBUSWrapper.turnInteriorLightsOn();
104 | userOutput = "Turning on interior lights!";
105 | }
106 | }else if(spokenText.contains("exterior")){
107 |
108 | }
109 | }else if(spokenText.contains("trunk")){
110 | if(spokenText.contains("open") || spokenText.contains("pop")){
111 | IBUSWrapper.openTrunk();
112 | userOutput = "Opening the trunk!";
113 | }
114 | }else if(spokenText.contains("unlock")){
115 | IBUSWrapper.unlockCar();
116 | userOutput = "Unlocking the car!";
117 | }else if(spokenText.contains("lock") || spokenText.contains("click click")){
118 | IBUSWrapper.lockCar();
119 | userOutput = "Locking the car!";
120 | }else if(spokenText.contains("sunroof")){
121 | if(spokenText.contains("open")){
122 | IBUSWrapper.toggleSunroof(true);
123 | userOutput = "Opening the sunroof!";
124 | }else{
125 | IBUSWrapper.toggleSunroof(false);
126 | userOutput = "Closing the sunroof!";
127 | }
128 | }else if(spokenText.contains("volume") || spokenText.contains("turn it")){
129 | if(spokenText.contains("up")){
130 | if(spokenText.contains("way")){
131 | IBUSWrapper.volumeUp();
132 | IBUSWrapper.volumeUp();
133 | IBUSWrapper.volumeUp();
134 | IBUSWrapper.volumeUp();
135 | IBUSWrapper.volumeUp();
136 | IBUSWrapper.volumeUp();
137 | IBUSWrapper.volumeUp();
138 | IBUSWrapper.volumeUp();
139 | userOutput = "Turning it way up!";
140 | }else {
141 | IBUSWrapper.volumeUp();
142 | userOutput = "Turning it up!";
143 | }
144 | }else if(spokenText.contains("down")){
145 | if (spokenText.contains("way")){
146 | IBUSWrapper.volumeDown();
147 | IBUSWrapper.volumeDown();
148 | IBUSWrapper.volumeDown();
149 | IBUSWrapper.volumeDown();
150 | IBUSWrapper.volumeDown();
151 | IBUSWrapper.volumeDown();
152 | IBUSWrapper.volumeDown();
153 | IBUSWrapper.volumeDown();
154 | userOutput = "Turning it way down!";
155 | }else{
156 | IBUSWrapper.volumeDown();
157 | userOutput = "Turning it down!";
158 | }
159 | }
160 | }else if(spokenText.contains("hazards")){
161 | IBUSWrapper.toggleHazards();
162 | userOutput = "Toggling the hazard lights!";
163 | }else if(spokenText.contains("seat")){
164 | if(spokenText.contains("upper")){
165 | if(spokenText.contains("back")){
166 | IBUSWrapper.moveDriverSeatUpperPortion(false);
167 | userOutput = "Moving the upper driver seat back!";
168 | }else if(spokenText.contains("forward")){
169 | IBUSWrapper.moveDriverSeatUpperPortion(true);
170 | userOutput = "Moving the upper driver seat forward!";
171 | }
172 | }else{
173 | if(spokenText.contains("back")){
174 | IBUSWrapper.moveDriverSeat(false);
175 | userOutput = "Moving the driver seat back!";
176 | }else if(spokenText.contains("forward")){
177 | IBUSWrapper.moveDriverSeat(true);
178 | userOutput = "Moving the driver seat forward!";
179 | }
180 | }
181 | }else{
182 | userOutput = "Sorry, I didn't catch: " + spokenText;
183 | }
184 |
185 | // cache last command
186 | LAST_COMMAND = spokenText;
187 |
188 | // return response to user
189 | return userOutput;
190 | }
191 |
192 | }
193 |
--------------------------------------------------------------------------------