# Device Type Schema - Terra-View

## Overview

Terra-View uses a single roster table to manage three different device types. The `device_type` field is the primary discriminator that determines which fields are relevant for each unit.

## Official device_type Values

As of **Terra-View v0.4.3**, the following device_type values are standardized:

### 1. `"seismograph"` (Default)
**Purpose**: Seismic monitoring devices

**Applicable Fields**:
- Common: id, unit_type, deployed, retired, note, project_id, location, address, coordinates
- Specific: last_calibrated, next_calibration_due, deployed_with_modem_id

**Examples**:
- `BE1234` - Series 3 seismograph
- `UM12345` - Series 4 Micromate unit
- `SEISMO-001` - Custom seismograph

**Unit Type Values**:
- `series3` - Series 3 devices (default)
- `series4` - Series 4 devices
- `micromate` - Micromate devices

---

### 2. `"modem"`
**Purpose**: Field modems and network equipment

**Applicable Fields**:
- Common: id, unit_type, deployed, retired, note, project_id, location, address, coordinates
- Specific: ip_address, phone_number, hardware_model

**Examples**:
- `MDM001` - Field modem
- `MODEM-2025-01` - Network modem
- `RAVEN-XTV-01` - Specific modem model

**Unit Type Values**:
- `modem` - Generic modem
- `raven-xtv` - Raven XTV model
- Custom values for specific hardware

---

### 3. `"slm"` ⭐
**Purpose**: Sound level meters (Rion NL-43/NL-53)

**Applicable Fields**:
- Common: id, unit_type, deployed, retired, note, project_id, location, address, coordinates
- Specific: slm_host, slm_tcp_port, slm_ftp_port, slm_model, slm_serial_number, slm_frequency_weighting, slm_time_weighting, slm_measurement_range, slm_last_check, deployed_with_modem_id

**Examples**:
- `SLM-43-01` - NL-43 sound level meter
- `NL43-001` - NL-43 unit
- `NL53-002` - NL-53 unit

**Unit Type Values**:
- `nl43` - Rion NL-43 model
- `nl53` - Rion NL-53 model

---

## Migration from Legacy Values

### Deprecated Values

The following device_type values have been **deprecated** and should be migrated:

- ❌ `"sound_level_meter"` → ✅ `"slm"`

### How to Migrate

Run the standardization migration script to update existing databases:

```bash
cd /home/serversdown/tmi/terra-view
python3 backend/migrate_standardize_device_types.py
```

This script:
- Converts all `"sound_level_meter"` values to `"slm"`
- Is idempotent (safe to run multiple times)
- Shows before/after distribution of device types
- No data loss

---

## Database Schema

### RosterUnit Model (`backend/models.py`)

```python
class RosterUnit(Base):
    """
    Supports multiple device types:
    - "seismograph" - Seismic monitoring devices (default)
    - "modem" - Field modems and network equipment
    - "slm" - Sound level meters (NL-43/NL-53)
    """
    __tablename__ = "roster"

    # Core fields (all device types)
    id = Column(String, primary_key=True)
    unit_type = Column(String, default="series3")
    device_type = Column(String, default="seismograph")  # "seismograph" | "modem" | "slm"
    deployed = Column(Boolean, default=True)
    retired = Column(Boolean, default=False)
    # ... other common fields

    # Seismograph-specific
    last_calibrated = Column(Date, nullable=True)
    next_calibration_due = Column(Date, nullable=True)

    # Modem-specific
    ip_address = Column(String, nullable=True)
    phone_number = Column(String, nullable=True)
    hardware_model = Column(String, nullable=True)

    # SLM-specific
    slm_host = Column(String, nullable=True)
    slm_tcp_port = Column(Integer, nullable=True)
    slm_ftp_port = Column(Integer, nullable=True)
    slm_model = Column(String, nullable=True)
    slm_serial_number = Column(String, nullable=True)
    slm_frequency_weighting = Column(String, nullable=True)
    slm_time_weighting = Column(String, nullable=True)
    slm_measurement_range = Column(String, nullable=True)
    slm_last_check = Column(DateTime, nullable=True)

    # Shared fields (seismograph + SLM)
    deployed_with_modem_id = Column(String, nullable=True)  # FK to modem
```

---

## API Usage

### Adding a New Unit

**Seismograph**:
```bash
curl -X POST http://localhost:8001/api/roster/add \
  -F "id=BE1234" \
  -F "device_type=seismograph" \
  -F "unit_type=series3" \
  -F "deployed=true"
```

**Modem**:
```bash
curl -X POST http://localhost:8001/api/roster/add \
  -F "id=MDM001" \
  -F "device_type=modem" \
  -F "ip_address=192.0.2.10" \
  -F "phone_number=+1-555-0100"
```

**Sound Level Meter**:
```bash
curl -X POST http://localhost:8001/api/roster/add \
  -F "id=SLM-43-01" \
  -F "device_type=slm" \
  -F "slm_host=63.45.161.30" \
  -F "slm_tcp_port=2255" \
  -F "slm_model=NL-43"
```

### CSV Import Format

```csv
unit_id,unit_type,device_type,deployed,slm_host,slm_tcp_port,slm_model
SLM-43-01,nl43,slm,true,63.45.161.30,2255,NL-43
SLM-43-02,nl43,slm,true,63.45.161.31,2255,NL-43
BE1234,series3,seismograph,true,,,
MDM001,modem,modem,true,,,
```

---

## Frontend Behavior

### Device Type Selection

**Templates**: `unit_detail.html`, `roster.html`

```html
<select name="device_type">
    <option value="seismograph">Seismograph</option>
    <option value="modem">Modem</option>
    <option value="slm">Sound Level Meter</option>
</select>
```

### Conditional Field Display

JavaScript functions check `device_type` to show/hide relevant fields:

```javascript
function toggleDetailFields() {
    const deviceType = document.getElementById('device_type').value;

    if (deviceType === 'seismograph') {
        // Show calibration fields
    } else if (deviceType === 'modem') {
        // Show network fields
    } else if (deviceType === 'slm') {
        // Show SLM configuration fields
    }
}
```

---

## Code Conventions

### Always Use Lowercase

✅ **Correct**:
```python
if unit.device_type == "slm":
    # Handle sound level meter
```

❌ **Incorrect**:
```python
if unit.device_type == "SLM":  # Wrong - case sensitive
if unit.device_type == "sound_level_meter":  # Deprecated
```

### Query Patterns

**Filter by device type**:
```python
# Get all SLMs
slms = db.query(RosterUnit).filter_by(device_type="slm").all()

# Get deployed seismographs
seismos = db.query(RosterUnit).filter_by(
    device_type="seismograph",
    deployed=True
).all()

# Get all modems
modems = db.query(RosterUnit).filter_by(device_type="modem").all()
```

---

## Testing

### Verify Device Type Distribution

```bash
# Quick check
sqlite3 data/seismo_fleet.db "SELECT device_type, COUNT(*) FROM roster GROUP BY device_type;"

# Detailed view
sqlite3 data/seismo_fleet.db "SELECT id, device_type, unit_type, deployed FROM roster ORDER BY device_type, id;"
```

### Check for Legacy Values

```bash
# Should return 0 rows after migration
sqlite3 data/seismo_fleet.db "SELECT id FROM roster WHERE device_type = 'sound_level_meter';"
```

---

## Version History

- **v0.4.3** (2026-01-16) - Standardized device_type values, deprecated `"sound_level_meter"` → `"slm"`
- **v0.4.0** (2026-01-05) - Added SLM support with `"sound_level_meter"` value
- **v0.2.0** (2025-12-03) - Added modem device type
- **v0.1.0** (2024-11-20) - Initial release with seismograph-only support

---

## Related Documentation

- [README.md](../README.md) - Main project documentation with data model
- [DEVICE_TYPE_SLM_SUPPORT.md](DEVICE_TYPE_SLM_SUPPORT.md) - Legacy SLM implementation notes
- [SOUND_LEVEL_METERS_DASHBOARD.md](SOUND_LEVEL_METERS_DASHBOARD.md) - SLM dashboard features
- [SLM_CONFIGURATION.md](SLM_CONFIGURATION.md) - SLM device configuration guide
