# Modem Integration System

## Overview

The modem integration system allows Sound Level Meters (SLMs) and Seismographs to be deployed with network connectivity modems. Instead of storing IP addresses directly on each device, units are assigned to modems which provide the network connection. This enables:

- Centralized modem management and tracking
- IP address updates in one place
- Future modem API integration for diagnostics
- Proper asset tracking for network equipment

## Architecture

### Database Design

**Modem Units**: Stored as `RosterUnit` with `device_type = "modem"`

**Modem-Specific Fields**:
- `ip_address`: Network IP address or hostname
- `phone_number`: Cellular phone number (if applicable)
- `hardware_model`: Modem hardware (e.g., "Raven XTV", "Sierra Wireless AirLink")

**Device Assignment**:
- Both SLMs and Seismographs use `deployed_with_modem_id` to reference their modem
- This is a foreign key to another `RosterUnit.id` where `device_type = "modem"`

### How It Works

1. **Create Modem Units**
   - Add modems as regular roster units with `device_type = "modem"`
   - Set IP address, phone number, and hardware model
   - Deploy or bench modems like any other asset

2. **Assign Devices to Modems**
   - When editing an SLM or Seismograph, select modem from dropdown
   - The `deployed_with_modem_id` field stores the modem ID
   - IP address is fetched from the assigned modem at runtime

3. **Runtime Resolution**
   - When SLM dashboard needs to connect to a device:
     1. Load SLM unit data
     2. Check `deployed_with_modem_id`
     3. Fetch modem unit
     4. Use modem's `ip_address` for connection
     5. Fallback to legacy `slm_host` if no modem assigned

## Implementation Details

### Frontend Changes

#### Unit Detail Page (`templates/unit_detail.html`)

**SLM Fields** (lines 320-375):
- Removed direct "Host (IP Address)" field
- Added "Deployed With Modem" dropdown selector
- Dropdown populated with all active modems via JavaScript
- Shows modem ID, IP address, and hardware model in dropdown

**JavaScript** (lines 456-485):
```javascript
async function loadModemsList() {
    // Fetches all modems from /api/roster/modems
    // Populates both seismograph and SLM modem dropdowns
    // Shows format: "modem-001 (192.168.1.100) - Raven XTV"
}
```

#### SLM Dashboard (`backend/routers/slm_dashboard.py`)

**Live View Endpoint** (lines 84-148):
```python
# Get modem information if assigned
modem = None
modem_ip = None
if unit.deployed_with_modem_id:
    modem = db.query(RosterUnit).filter_by(
        id=unit.deployed_with_modem_id,
        device_type="modem"
    ).first()
    if modem:
        modem_ip = modem.ip_address

# Fallback to direct slm_host (backward compatibility)
if not modem_ip and unit.slm_host:
    modem_ip = unit.slm_host
```

**Live View Template** (`templates/partials/slm_live_view.html`):
- Displays modem information in header
- Shows "via Modem: modem-001 (192.168.1.100)"
- Warning if no modem assigned

### Backend Changes

#### New API Endpoint (`backend/routers/roster_edit.py`)

```python
GET /api/roster/modems
```

Returns list of all non-retired modem units:
```json
[
    {
        "id": "modem-001",
        "ip_address": "192.168.1.100",
        "phone_number": "+1-555-0100",
        "hardware_model": "Raven XTV",
        "deployed": true
    }
]
```

Used by frontend to populate modem selection dropdowns.

#### Database Schema (`backend/models.py`)

**Modem Assignment Field** (line 44):
```python
# Shared by seismographs and SLMs
deployed_with_modem_id = Column(String, nullable=True)
```

**Modem Fields** (lines 46-48):
```python
ip_address = Column(String, nullable=True)
phone_number = Column(String, nullable=True)
hardware_model = Column(String, nullable=True)
```

**Legacy SLM Fields** (kept for backward compatibility):
```python
slm_host = Column(String, nullable=True)  # Deprecated - use modem instead
slm_tcp_port = Column(Integer, nullable=True)  # Still used
```

## Usage

### Creating Modem Units

1. Go to Fleet Roster
2. Click "Add Unit"
3. Set Device Type to "Modem"
4. Fill in:
   - Unit ID (e.g., "modem-001")
   - IP Address (e.g., "192.168.1.100")
   - Phone Number (if cellular)
   - Hardware Model (e.g., "Raven XTV")
   - Address/Coordinates (physical location)
5. Set Deployed status
6. Save

### Assigning Modem to SLM

1. Open SLM unit detail page
2. Click "Edit Unit"
3. Ensure Device Type is "Sound Level Meter"
4. In "Deployed With Modem" dropdown, select modem
5. Verify TCP Port (default 2255)
6. Save

### Assigning Modem to Seismograph

Same process - both device types use the same modem selection field.

## Test Data

Use the included script to create test modems:

```bash
python3 add_test_modems.py
```

This creates:
- **modem-001**: 192.168.1.100, Raven XTV → assigned to nl43-001
- **modem-002**: 192.168.1.101, Raven XTV → assigned to nl43-002
- **modem-003**: 192.168.1.102, Sierra Wireless → assigned to nl53-001
- **modem-004**: Spare modem (not deployed)

## Benefits

### For Operations

1. **Centralized IP Management**
   - Update modem IP once, affects all assigned devices
   - Easy to track which modem serves which devices
   - Inventory management for network equipment

2. **Asset Tracking**
   - Modems are first-class assets in the roster
   - Track deployment status, location, notes
   - Can bench/retire modems independently

3. **Future Capabilities**
   - Modem API integration (signal strength, data usage)
   - Automatic IP updates from DHCP/cellular network
   - Modem health monitoring
   - Remote modem diagnostics

### For Maintenance

1. **Easier Troubleshooting**
   - See which modem serves a device
   - Check modem status separately
   - Swap modems without reconfiguring devices

2. **Configuration Changes**
   - Change IP addresses system-wide
   - Move devices between modems
   - Test with backup modems

## Migration from Legacy System

### For Existing SLMs with Direct IP

Legacy SLMs with `slm_host` set still work:
- System checks `deployed_with_modem_id` first
- Falls back to `slm_host` if no modem assigned
- Logs fallback usage for visibility

### Migration Steps

1. Create modem units for each IP address
2. Assign SLMs to their modems
3. System will use modem IP automatically
4. Legacy `slm_host` can be cleared (optional)

Script `add_test_modems.py` demonstrates this:
```python
# Clear legacy field after modem assignment
slm.slm_host = None
slm.deployed_with_modem_id = "modem-001"
```

## Future Enhancements

### Near-term

1. **Modem Status Dashboard**
   - List all modems with connection status
   - Show which devices use each modem
   - Signal strength, data usage indicators

2. **Automatic IP Discovery**
   - Query cellular provider API for modem IPs
   - Auto-update IP addresses in database
   - Alert on IP changes

3. **Modem Health Monitoring**
   - Ping modems periodically
   - Check cellular signal quality
   - Data usage tracking

### Long-term

1. **Modem API Integration**
   - Direct modem management (Raven, Sierra APIs)
   - Remote reboot capability
   - Configuration backup/restore
   - Firmware updates

2. **Network Topology View**
   - Graphical view of modem-device relationships
   - Network health visualization
   - Troubleshooting tools

3. **Multi-Modem Support**
   - Failover between modems
   - Load balancing
   - Automatic fallback on modem failure

## API Reference

### Get Modems List

**Endpoint**: `GET /api/roster/modems`

**Response**:
```json
[
    {
        "id": "modem-001",
        "ip_address": "192.168.1.100",
        "phone_number": "+1-555-0100",
        "hardware_model": "Raven XTV",
        "deployed": true
    }
]
```

**Used By**:
- Unit detail page (modem dropdown)
- Future modem management dashboard

### Get Unit with Modem Info

**Endpoint**: `GET /api/roster/{unit_id}`

**Response** (for SLM):
```json
{
    "id": "nl43-001",
    "device_type": "slm",
    "deployed_with_modem_id": "modem-001",
    "slm_tcp_port": 2255,
    "slm_model": "NL-43",
    ...
}
```

Then fetch modem separately or use dashboard endpoint which resolves it automatically.

### SLM Live View (with modem resolution)

**Endpoint**: `GET /api/slm-dashboard/live-view/{unit_id}`

**Process**:
1. Loads SLM unit
2. Resolves `deployed_with_modem_id` to modem unit
3. Extracts `modem.ip_address`
4. Uses IP to connect to SLMM backend
5. Returns live view HTML with modem info

## Files Modified/Created

### Modified
1. `backend/models.py` - Clarified modem assignment field
2. `templates/unit_detail.html` - Added modem selector for SLMs
3. `backend/routers/roster_edit.py` - Added modems list endpoint
4. `backend/routers/slm_dashboard.py` - Modem resolution logic
5. `templates/partials/slm_live_view.html` - Display modem info

### Created
1. `add_test_modems.py` - Script to create test modems and assignments
2. `docs/MODEM_INTEGRATION.md` - This documentation

## Troubleshooting

### SLM shows "No modem assigned"

**Cause**: Unit has no `deployed_with_modem_id` set

**Solution**:
1. Edit the SLM unit
2. Select a modem from dropdown
3. Save

### Modem dropdown is empty

**Cause**: No modem units in database

**Solution**:
1. Create modem units first
2. Set `device_type = "modem"`
3. Ensure they're not retired

### Can't connect to SLM

**Possible Causes**:
1. Modem IP is incorrect
2. Modem is offline
3. SLM TCP port is wrong
4. Network routing issue

**Debug Steps**:
1. Check modem unit's IP address
2. Ping the modem IP
3. Check SLM's `slm_tcp_port` (default 2255)
4. Review logs for connection errors

---

**Version**: 1.0.0
**Date**: January 5, 2026
**Related**: SOUND_LEVEL_METERS_DASHBOARD.md, DEVICE_TYPE_SLM_SUPPORT.md
