# Meters API

Manage physical meters (compteurs) installed in properties — electricity, gas, water, heating, and cooling. Track readings, calculate consumption automatically, and integrate with the charge billing system.

## Quick Example

```bash
# Create an electricity meter
curl -X POST https://api.faireplace.com/api/meters \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "estate_id": "<estate_uuid>",
    "meter_type": "ELECTRICITY",
    "billing_type": "INDIVIDUAL",
    "serial_number": "EDF-2024-001234",
    "manufacturer": "Itron",
    "model_reference": "Linky G3",
    "installation_date": "2024-01-15",
    "unit": "KWH"
  }'

# Record a reading
curl -X POST https://api.faireplace.com/api/meters/<meter_id>/readings \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reading_date": "2024-02-01T10:00:00Z",
    "reading_type": "MANUAL",
    "value": 12345.67,
    "reader_name": "Jean Dupont"
  }'
```

## Common Workflows

### Set up metering for a property
1. `POST /api/meters` — install the physical meter
2. `POST /api/leases/{id}/meters` — link the meter to a lease
3. `POST /api/leases/{id}/charges/{id}/meters` — link the meter to a charge (for billing)
4. `POST /api/meters/{id}/readings` — record readings over time

### Monthly reading and billing
1. `POST /api/meters/{id}/readings` — record the reading (consumption auto-calculated)
2. `GET /api/meters/{id}/readings/latest` — verify the calculated consumption
3. Use the charge system for billing (see [Charges API](/leases/charges))

### Sub-meter setup
1. `POST /api/meters` — create the main building meter (`is_main_meter: true`)
2. `POST /api/meters` — create sub-meters with `parent_meter_id` pointing to the main meter
3. `GET /api/meters/{id}/sub-meters` — list all sub-meters for a parent

---

## Authentication

All endpoints require authentication:

```
Authorization: Bearer <API_KEY>
```

**Permissions:**
- Read operations: `PropertiesRead`
- Create/Update: `PropertiesWrite`
- Delete: `PropertiesDelete`

---

## Meter Types

| Type | Description | Unit |
|------|-------------|------|
| `ELECTRICITY` | Electrical consumption | KWH |
| `GAS` | Gas consumption | M3 |
| `WATER_HOT` | Hot water | M3 or L |
| `WATER_COLD` | Cold water | M3 or L |
| `HEATING` | Heating (calories) | KCAL |
| `COOLING` | Cooling/AC | BTU or KWH |

## Billing Types

| Type | Description | Use Case |
|------|-------------|----------|
| `INDIVIDUAL` | Meter for a single tenant | Apartment electricity meter |
| `COLLECTIVE` | Shared meter for the building | Building gas boiler meter |
| `SHARED` | Shared among specific tenants with ratios | Shared water meter (2 apartments) |
| `INCLUDED` | Consumption included in rent | Informational tracking only |

## Meter Status

| Status | Description |
|--------|-------------|
| `ACTIVE` | Meter is operational and readings can be recorded |
| `INACTIVE` | Meter is installed but not in use |
| `MAINTENANCE` | Meter is under maintenance |
| `REPLACED` | Meter has been replaced by a new one |
| `REMOVED` | Meter has been physically removed |

## Units

| Unit | Description |
|------|-------------|
| `KWH` | Kilowatt-hours (electricity) |
| `M3` | Cubic meters (gas, water) |
| `L` | Liters (water) |
| `KCAL` | Kilocalories (heating) |
| `BTU` | British Thermal Units (cooling) |

---

## Meter Model

| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | Unique identifier |
| `place_id` | UUID? | Place where the meter is installed |
| `estate_id` | UUID? | Estate (lot) where the meter is installed |
| `service_place_id` | UUID? | Associated service |
| `meter_type` | MeterType | Type of meter |
| `billing_type` | MeterBillingType | How the meter is used for billing |
| `serial_number` | string | Unique serial number (per tenant) |
| `manufacturer` | string? | Manufacturer name |
| `model_reference` | string? | Model reference |
| `installation_date` | date | Installation date |
| `last_inspection_date` | date? | Last inspection date |
| `next_inspection_date` | date? | Next planned inspection |
| `unit` | MeterUnit | Unit of measurement |
| `multiplier` | decimal | Reading multiplier (default: 1.0) |
| `status` | MeterStatus | Current status |
| `is_main_meter` | boolean | Whether this is a main meter |
| `parent_meter_id` | UUID? | Parent meter (for sub-meters) |
| `max_value` | decimal? | Max value for rollover detection |
| `precision_digits` | integer | Decimal precision (default: 2) |

---

## Meter Endpoints

### List Meters
**GET** `/api/meters`

**Query Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `place_id` | UUID | Filter by place |
| `estate_id` | UUID | Filter by estate |
| `service_place_id` | UUID | Filter by service |
| `meter_type` | string | Filter by type (ELECTRICITY, GAS, etc.) |
| `status` | string | Filter by status |
| `billing_type` | string | Filter by billing type |
| `is_main_meter` | boolean | Filter main meters vs. sub-meters |
| `page` | integer | Page number (default: 1) |
| `limit` | integer | Items per page (default: 50, max: 500) |

**Response:** `200 OK`
```json
[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "estate_id": "880e8400-e29b-41d4-a716-446655440000",
    "meter_type": "ELECTRICITY",
    "billing_type": "INDIVIDUAL",
    "serial_number": "EDF-2024-001234",
    "manufacturer": "Itron",
    "model_reference": "Linky G3",
    "installation_date": "2024-01-15",
    "unit": "KWH",
    "multiplier": 1.0,
    "status": "ACTIVE",
    "is_main_meter": false,
    "precision_digits": 2,
    "created_at": "2024-01-15T10:00:00Z",
    "updated_at": "2024-01-15T10:00:00Z"
  }
]
```

### Create Meter
**POST** `/api/meters`

**Response:** `201 Created`

### Get / Update / Delete Meter
- **GET** `/api/meters/{id}` — `200 OK`
- **PUT** `/api/meters/{id}` — `200 OK`
- **DELETE** `/api/meters/{id}` — `204 No Content`

### List Sub-Meters
**GET** `/api/meters/{id}/sub-meters`

List all sub-meters attached to a parent meter. Used for meter hierarchy (e.g., main building meter with individual apartment sub-meters).

**Response:** `200 OK` — Array of `Meter`

---

## Meter Validation Rules

| Rule | Description |
|------|-------------|
| Serial number | Cannot be empty, must be unique per tenant |
| Installation date | Cannot be in the future |
| Inspection dates | `last_inspection_date` >= `installation_date`, < `next_inspection_date` |
| Multiplier | Must be > 0 |
| Max value | Must be > 0 (if set) |
| Location | At least one of `place_id` or `estate_id` is required |

---

## Meter Readings

Readings track consumption values from meters at specific points in time.

### Reading Input Types

| Type | Description |
|------|-------------|
| `MANUAL` | Manual entry by a person |
| `AUTOMATIC` | Automatic reading from connected meter |
| `PHOTO` | Reading from a photo (OCR) |
| `ESTIMATED` | Estimated value |

### MeterReading Model

| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | Unique identifier |
| `meter_id` | UUID | Associated meter |
| `lease_id` | UUID? | Optional lease association |
| `reading_date` | datetime | When the reading was taken |
| `reading_type` | MeterReadingInputType | How the reading was captured |
| `value` | decimal | The meter reading value |
| `previous_value` | decimal? | Previous reading (auto-populated) |
| `consumption` | decimal? | Calculated consumption (auto-populated) |
| `is_estimated` | boolean | Whether the reading is an estimate |
| `is_billing_reading` | boolean | Whether used for billing |
| `photo_url` | string? | URL to photo of the meter |
| `reader_name` | string? | Name of the reader |

### Automatic Consumption Calculation

When you create a reading, the system automatically:
1. Fetches the **latest reading** for the meter
2. Sets `previous_value` to the latest reading's value
3. Calculates `consumption`:
   - Normal: `(value - previous_value) * multiplier`
   - With rollover (when `value < previous_value` and `max_value` is set): `(max_value - previous_value + value) * multiplier`

### Anomaly Detection

The system flags consumption anomalies when the calculated consumption deviates **more than 50%** from the average:
- **High threshold**: consumption > 150% of average
- **Low threshold**: consumption < 50% of average

### Endpoints

#### List Readings
**GET** `/api/meters/{meter_id}/readings`

**Query Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `reading_type` | string | Filter by reading type |
| `is_billing_reading` | boolean | Filter billing readings |
| `start_date` | datetime | Start of date range |
| `end_date` | datetime | End of date range |
| `page` | integer | Page number |
| `limit` | integer | Items per page (default: 50, max: 500) |

**Response:** `200 OK`
```json
{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440010",
      "meter_id": "550e8400-e29b-41d4-a716-446655440000",
      "reading_date": "2024-02-01T10:00:00Z",
      "reading_type": "MANUAL",
      "value": 12345.67,
      "previous_value": 12100.00,
      "consumption": 245.67,
      "is_estimated": false,
      "is_billing_reading": false,
      "reader_name": "Jean Dupont",
      "created_at": "2024-02-01T10:05:00Z",
      "updated_at": "2024-02-01T10:05:00Z"
    }
  ],
  "total": 1
}
```

#### Create Reading
**POST** `/api/meters/{meter_id}/readings`

**Response:** `201 Created` — MeterReading with auto-populated `previous_value` and `consumption`

#### Get Latest Reading
**GET** `/api/meters/{meter_id}/readings/latest`

Returns the most recent reading for the meter.

**Response:** `200 OK` — MeterReading

#### Get / Update / Delete Reading
- **GET** `/api/meters/{meter_id}/readings/{reading_id}` — `200 OK`
- **PUT** `/api/meters/{meter_id}/readings/{reading_id}` — `200 OK`
- **DELETE** `/api/meters/{meter_id}/readings/{reading_id}` — `204 No Content`

---

## Integration with Leases & Charges

Meters are linked to the charge system through two association resources:

### Lease Meters (`/leases/{id}/meters`)
Link a physical meter to a lease to track which meters are active during the lease period. See [Charges API — Lease Meters](/leases/charges#lease-meters).

### Charge Meters (`/leases/{id}/charges/{id}/meters`)
Link a meter to a specific charge to define how consumption is calculated for billing. See [Charges API — Charge Meters](/leases/charges#charge-meters).

### Database Functions

The system provides SQL functions for advanced meter operations:
- **`calculate_meter_consumption_for_period`** — Total consumption, average daily, reading count over a period
- **`validate_lease_meter_period`** — Validates meter status and period against lease dates
- **`validate_meter_billing_ratios`** — Ensures total billing ratios for a meter don't exceed 100%
- **`calculate_meter_based_charge`** — Calculates charge amount from meter readings (supports DIRECT, WEIGHTED, SHARED, FIXED_ALLOCATION methods)

---

## Error Responses

| Status | Condition |
|--------|-----------|
| `400 Bad Request` | Empty serial number; future installation date; invalid inspection date order; multiplier <= 0; reading value < 0 |
| `403 Forbidden` | Insufficient permissions |
| `404 Not Found` | Meter or reading not found |
| `409 Conflict` | Duplicate serial number; meter referenced by active lease |
| `500 Internal Server Error` | Unexpected error |

---

## Related

- **[Charges API](/leases/charges)** — Charge billing with meter integration
- **[Distribution Keys](/leases/distribution-keys)** — Cost allocation with metered consumption
- **[Place Services](/properties/services)** — Link services to meters
