A modern, reactive Smart Home management application built with Jetpack Compose. This app allows users to monitor sensors, manage device schedules, control multi-channel relays, receive system notifications, and dynamically switch backend API and WebSocket servers in real time.
- Real-Time Monitoring: View current temperature, humidity, battery levels, and link quality powered by live Symfony API and WebSocket updates.
- Thermostat Control: Interactive circular dial to adjust the "Set" temperature (Range: 0ยฐC to 30ยฐC).
- Unit Conversion: Toggle between Celsius (ยฐC) and Fahrenheit (ยฐF) across the entire app.
- Visual Alerts:
- Highlights sensors requiring heating (Current < Set).
- Red alerts for low battery (< 20%).
- Orange alerts for stale data (> 1 hour).
- Switch Management: Define active time windows for devices like Heaters, Boilers, and Garden Lights.
- Precise Timing: Set "From" and "Till" hours (0-23) using an interactive slider interface.
- Overlapping Validation: Client-side and server-side validation to prevent overlapping schedule windows.
- Multi-Channel Control: Support for modules with 1 to 16 independent switches.
- Responsive Toggles: Instant on/off switching with reactive UI updates.
- Custom Labels: Each switch within a relay has its own unique name and state.
- Production Push Notifications: Full Firebase Cloud Messaging (FCM) integration and background event processing.
- Inbox System: Real-time unread badges on the navigation bar, read/unread states, and clear-all functionality.
- Dynamic Endpoint Switch: Switch from default
.envAPI URL (BuildConfig.API_BASE_URL) to any custom local server endpoint (e.g.http://192.168.1.100:8000/api/) directly from the UI. - Custom & Auto-Derived WebSocket Endpoint: The WebSocket URL automatically adapts to the selected server host (e.g.,
ws://192.168.1.100:8080/?clientId=...). Optionally, users can specify an explicit custom WebSocket URL. - Accessible Everywhere: Available via the settings icon on both the Login Screen and Dashboard TopAppBar.
- Persistent Preferences: API and WebSocket endpoint configurations are stored securely in
DataStorepreferences and saved across logouts and app restarts. - Instant Payload Updates: Real-time WebSocket payloads trigger 0ms in-memory UI state updates combined with background API sync.
- Master-Detail Split View: On tablets (
>= 600.dpscreen width), the Sensors tab displays a side-by-side split screenโSensor list on the left pane (360dp) and live interactive Thermostat controls on the right pane. - Persistent Navigation Menu: The main navigation menu remains visible at all times on tablets when interacting with sensors.
- Adaptive Multi-Column Grids: Relays and Device Schedules automatically format into 2 or 3 responsive grid columns on large screens instead of stretching cards.
- Centered Form Containers: Login and setting dialogs are constrained to an optimal width (
widthIn(max = 440.dp)) centered on screen for ergonomics.
-
From Terminal:
-
Pixel 8 (Phone):
emulator -avd Pixel_8
-
Pixel Tablet (Tablet):
emulator -avd Pixel_Tablet
-
Pixel 8 (Phone):
-
From Android Studio: Open Tools
$\rightarrow$ Device Manager$\rightarrow$ Click Play next toPixel_8orPixel_Tablet. -
Creating a new AVD (if needed):
# Create Pixel 8 AVD avdmanager create avd -n Pixel_8 -k "system-images;android-34;google_apis;arm64-v8a" -d "pixel_8" # Create Pixel Tablet AVD avdmanager create avd -n Pixel_Tablet -k "system-images;android-34;google_apis;arm64-v8a" -d "pixel_tablet"
With the emulator running:
# Build and install on active emulator
./gradlew installDebug
# Launch main activity
adb shell am start -n com.upasmarthome.app/com.smarthome.MainActivity-
Enable Developer Options & USB Debugging:
- On your phone: Settings
$\rightarrow$ About Phone$\rightarrow$ Tap Build Number 7 times. - Go to Settings
$\rightarrow$ System / Additional Settings$\rightarrow$ Developer Options$\rightarrow$ Enable USB Debugging.
- On your phone: Settings
-
Connect Phone via USB:
- Plug phone into Mac via USB and accept "Allow USB Debugging?" prompt.
- Verify connection:
adb devices
-
Install the App:
./gradlew installDebug
- Method 1 (Rotate Running Phone Emulator to Landscape):
Rotate your running phone emulator to Landscape mode to increase width past
600dpand trigger the tablet layout:- Click the Rotate button on the emulator toolbar.
- Or run via terminal:
adb shell settings put system accelerometer_rotation 0 adb shell settings put system user_rotation 1
- Method 2 (Run Dedicated
Pixel_TabletEmulator):emulator -avd Pixel_Tablet ./gradlew installDebug adb shell am start -n com.upasmarthome.app/com.smarthome.MainActivity
- Android Studio (Ladybug or newer recommended).
- JDK 17 or higher.
- Android SDK 34 (API Level 34).
- Clone the repository:
git clone <repository-url> cd androidapp
- Configure Environment Variables:
- Copy
.env.exampleto.env:cp .env.example .env
- Copy
- Firebase Configuration:
- Package name:
com.upasmarthome.app. - Copy
app/google-services.json.exampletoapp/google-services.jsonand insert your Firebase credentials:cp app/google-services.json.example app/google-services.json
- Package name:
- Build & Run:
./gradlew assembleDebug
The Android app communicates with the Symfony 8.1 API Platform backend (smarthomeapi) via REST HTTP endpoints and a real-time WebSocket push listener.
| Method | Endpoint | Description | Payload / Query |
|---|---|---|---|
POST |
/api/login |
Authenticates device credentials | {"serialNumber": "SN123456", "otp": "12345678"} |
POST |
/api/register |
Registers a new device serial number | {"serialNumber": "SN123456", "otp": "12345678"} |
GET |
/api/sensors |
Fetches all smart temperature & humidity sensors | Auth: Basic (serialNumber:otp) |
PATCH |
/api/sensors/{id} |
Updates setpoint temperature for a sensor | {"setTemp": 22.5} |
GET |
/api/relays |
Fetches all multi-channel relay modules | Auth: Basic (serialNumber:otp) |
POST |
/api/relays/{relayId}/toggle/{switchId} |
Toggles state of a specific switch | Auth: Basic (serialNumber:otp) |
GET |
/api/schedules |
Fetches active device operation schedules | Auth: Basic (serialNumber:otp) |
PATCH |
/api/schedules/{id} |
Updates schedule time window | {"fromHour": 7, "toHour": 9} |
GET |
/api/notifications |
Fetches inbox notifications | Auth: Basic (serialNumber:otp) |
PATCH |
/api/notifications/{id} |
Marks a notification as read | {"isRead": true} |
DELETE |
/api/notifications |
Clears all notifications in inbox | Auth: Basic (serialNumber:otp) |
ws://127.0.0.1:8080/?clientId=SN123456
{
"event": "<EVENT_NAME>",
"data": { ... }
}| Event Name | Example data Payload |
App Action Taken |
|---|---|---|
refresh_sensors |
{"sensorId": "1", "setTemp": 22.5} |
0ms immediate in-memory setpoint update + background GET /api/sensors fetch |
refresh_relays |
{"relayId": "r1", "switchId": "rs1", "isOn": true} |
0ms immediate in-memory switch state toggle + background GET /api/relays fetch |
refresh_schedules |
{"scheduleId": "s1"} |
Background GET /api/schedules fetch |
refresh_notifications |
{} |
Background GET /api/notifications fetch |
refresh_all |
{} |
Refreshes all 4 data domains simultaneously |
Represents smart temperature and humidity sensors in the system.
{
"id": "1",
"name": "Living Room",
"currentTemp": 22.5,
"setTemp": 22.0,
"humidity": 45.0,
"batteryLevel": 85,
"linkQuality": 200,
"lastUpdated": 1722019200000
}id(String): Unique identifier of the sensor entity.name(String): Human-readable room/device display name.currentTemp(Float): Live measured temperature in Celsius.setTemp(Float): Desired target thermostat setpoint temperature in Celsius.humidity(Float): Relative humidity percentage (0-100%).batteryLevel(Int): Remaining battery percentage (0-100%). Below20%triggers a low-battery alert.linkQuality(Int): Signal link quality indicator (LQI:0-255).lastUpdated(Long): Millisecond Unix timestamp of last received reading. Older than 1 hour triggers a stale data alert.
Represents multi-channel switch relay hardware modules.
{
"id": "r1",
"name": "Living Room Relay",
"switches": [
{ "id": "rs1", "label": "Main Light", "isOn": true },
{ "id": "rs2", "label": "Socket 1", "isOn": false }
]
}id(String): Unique identifier of the relay controller module.name(String): Display name of the relay controller.switches(Array<RelaySwitch>): List of individual switch channels (1to16channels).id(String): Unique channel ID.label(String): Label describing connected appliance (e.g. "Main Light").isOn(Boolean): Active state (true= ON,false= OFF).
Defines automated active operation windows for devices.
{
"id": "s1",
"sensorName": "Main Heater",
"fromHour": 7,
"toHour": 9
}id(String): Unique schedule ID.sensorName(String): Name of target hardware device or zone.fromHour(Int): Operation start hour (0to23).toHour(Int): Operation end hour (0to23).
System inbox alerts and push notifications.
{
"id": "1",
"title": "Security Alert",
"message": "Motion detected in Living Room at 2:00 AM.",
"timestamp": 1722015600000,
"isRead": false
}id(String): Unique notification ID.title(String): Alert title.message(String): Detailed alert message body.timestamp(Long): Millisecond Unix timestamp of alert creation.isRead(Boolean): Read/Unread inbox status flag.
// Login / Register Request Body
{
"serialNumber": "SN123456",
"otp": "12345678"
}
// Login Response Body
{
"token": "mock_jwt_token_SN123456"
}serialNumber(String): Registered device serial number.otp(String): 8-digit numeric one-time authorization PIN.token(String): JWT Bearer authentication token.
com.smarthome.data: Repositories (ProductionAuthRepository,ProductionSensorRepository,ProductionNotificationRepository),AuthPreferences, andSmartHomeFirebaseService.com.smarthome.data.network:ApiService,AuthInterceptor,DynamicBaseUrlInterceptor, andNetworkClient.com.smarthome.ui.components: UI dialogs likeCustomApiServerDialog.com.smarthome.ui.dashboard: Main feature views (DashboardScreen,SchedulesScreen,RelaysScreen,NotificationsScreen,ThermostatControl).com.smarthome.ui.auth:LoginScreenwith custom server settings.com.smarthome.navigation: Navigation host and routing logic.com.smarthome.MainActivity: Entry point and FCM initialization.
- Locate the Device Token in Logcat (filter by
FCM). - Send test notification via Firebase Console or cURL to the Symfony API.
- The app receives and displays system alerts in real-time.