Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
253 changes: 253 additions & 0 deletions connections/_schema/mapping_rules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "RoamCore Capability Mapping Rules",
"description": "Declarative rules that turn raw Home Assistant entity_ids into canonical RoamCore capability ids. The rule list is consumed by the pure-Python mapper in homeassistant/custom_components/roamcore/capability_mapper.py. Every rule's `canonical_capability` MUST exist in canonical_capabilities.json (validated by validate_mapping_rules). Pattern syntax is Python re (matched against the entity_id via re.fullmatch). Naming follows docs/reference/rc-entity-naming.md: contract ids start with `rc_`, no vendor names in contract ids, but the `source_pattern` is allowed to mention vendor names (it is matching raw upstream entity_ids, not contract ids).",
"version": 1,
"rules": [
{
"id": "victron_battery_soc",
"source_pattern": "sensor\\.(vt_|victron).*battery_?(state_of_charge|soc|state)(_percent|_pct)?",
"canonical_capability": "rc_power_battery_soc",
"weight": 95,
"description": "Victron (any model) battery state of charge → leisure battery percentage"
},
{
"id": "victron_battery_voltage",
"source_pattern": "sensor\\.(vt_|victron).*battery_?(voltage|v)(_v)?",
"canonical_capability": "rc_power_battery_voltage",
"weight": 95,
"description": "Victron (any model) battery terminal voltage"
},
{
"id": "victron_battery_current",
"source_pattern": "sensor\\.(vt_|victron).*battery_?(current|i)(_a)?",
"canonical_capability": "rc_power_battery_current",
"weight": 95,
"description": "Victron (any model) battery current (positive = charging)"
},
{
"id": "renogy_battery_soc",
"source_pattern": "sensor\\.renogy.*soc",
"canonical_capability": "rc_power_battery_soc",
"weight": 90,
"description": "Renogy battery monitor → leisure battery percentage (fallback to victron rule)"
},
{
"id": "victron_solar_power",
"source_pattern": "sensor\\.(vt_|victron_)?solar_?(power|w)(_w)?",
"canonical_capability": "rc_power_solar_power",
"weight": 95,
"description": "Victron / generic solar charging power in watts"
},
{
"id": "renogy_solar_power",
"source_pattern": "sensor\\.renogy.*solar.*(power|w)",
"canonical_capability": "rc_power_solar_power",
"weight": 90,
"description": "Renogy solar controller → solar charging power"
},
{
"id": "victron_shore_connected",
"source_pattern": "binary_sensor\\.(vt_|victron_)?shore.*(connected|plug)",
"canonical_capability": "rc_power_shore_connected",
"weight": 95,
"description": "Whether the van is plugged into shore / mains power"
},
{
"id": "interior_lights_state",
"source_pattern": "switch\\.(cabin[_a-z0-9]*|interior[_a-z0-9]*|main_lights|rc_lighting_interior)",
"canonical_capability": "rc_lighting_interior_state",
"weight": 90,
"description": "Interior cabin lights on/off (cabin / interior / main_lights, with any suffix)"
},
{
"id": "approach_lights_state",
"source_pattern": "switch\\.(porch_light[a-z0-9_]*|awning_light[a-z0-9_]*|approach_light[a-z0-9_]*|exterior_light[a-z0-9_]*|rc_lighting_approach[a-z_0-9]*)",
"canonical_capability": "rc_lighting_approach_state",
"weight": 90,
"description": "Approach / porch / awning lights on/off"
},
{
"id": "indoor_temperature",
"source_pattern": "sensor\\.(indoor_temp[a-z0-9_]*|indoor_temperature[a-z0-9_]*|cabin_temp[a-z0-9_]*|cabin_temperature[a-z0-9_]*|rc_climate_indoor[a-z_0-9]*)",
"canonical_capability": "rc_climate_indoor_temperature",
"weight": 90,
"description": "Indoor cabin temperature (degrees C)"
},
{
"id": "hvac_main_state",
"source_pattern": "switch\\.(hvac_main[a-z0-9_]*|hvac[a-z0-9_]*|webasto[a-z0-9_]*|air_conditioner[a-z0-9_]*|ac_main[a-z0-9_]*|heater_main[a-z0-9_]*|rc_climate_hvac[a-z_0-9]*)",
"canonical_capability": "rc_climate_hvac_state",
"weight": 90,
"description": "Primary HVAC / heater / AC / Webasto on/off"
},
{
"id": "fresh_water_level",
"source_pattern": "sensor\\.(fresh_water[a-z0-9_]*|fresh_tank[a-z0-9_]*|water_fresh[a-z0-9_]*|potable_water[a-z0-9_]*|rc_water_fresh[a-z_0-9]*)",
"canonical_capability": "rc_water_fresh_level",
"weight": 90,
"description": "Fresh / potable water tank level (percent)"
},
{
"id": "water_pump_state",
"source_pattern": "switch\\.(water_pump[a-z0-9_]*|shurflo[a-z0-9_]*|rc_water_pump[a-z_0-9]*)",
"canonical_capability": "rc_water_pump_state",
"weight": 90,
"description": "12V water pump on/off"
},
{
"id": "gps_latitude",
"source_pattern": "(sensor|device_tracker)\\.(gps_lat|vehicle_lat|vt_vehicle_lat|rc_position_lat|location_latitude)",
"canonical_capability": "rc_position_lat",
"weight": 85,
"description": "Vehicle GPS latitude (degrees)"
},
{
"id": "gps_longitude",
"source_pattern": "(sensor|device_tracker)\\.(gps_lon|gps_lng|vehicle_lon|vt_vehicle_lon|rc_position_lon|location_longitude)",
"canonical_capability": "rc_position_lon",
"weight": 85,
"description": "Vehicle GPS longitude (degrees)"
},
{
"id": "vehicle_device_tracker",
"source_pattern": "device_tracker\\.(vt_vehicle|roamcore_vehicle|van_gps|vehicle)",
"canonical_capability": "rc_position_lat",
"weight": 70,
"description": "device_tracker-style GPS — primary mapping to lat (lat + lon both available in the same tracker)"
},
{
"id": "internet_reachable",
"source_pattern": "binary_sensor\\.(rc_net_internet_reachable|internet_reachable|wan_reachable|net_internet)",
"canonical_capability": "rc_network_internet_reachable",
"weight": 95,
"description": "Whether the van has any working internet path (LTE, Starlink, Wi-Fi)"
},
{
"id": "wan_ip_address",
"source_pattern": "sensor\\.(rc_net_wan_ip|wan_ip|public_ip|external_ip)",
"canonical_capability": "rc_network_wan_ip",
"weight": 90,
"description": "Current public / WAN IP address of the active internet link"
},
{
"id": "roamcore_contract_power_soc",
"source_pattern": "sensor\\.rc_power_battery_soc",
"canonical_capability": "rc_power_battery_soc",
"weight": 100,
"description": "Self-mapping of an existing canonical tile (identity rule — passes through if a downstream template already published one)"
},
{
"id": "roamcore_contract_power_voltage",
"source_pattern": "sensor\\.rc_power_battery_voltage",
"canonical_capability": "rc_power_battery_voltage",
"weight": 100,
"description": "Self-mapping of an existing canonical tile"
},
{
"id": "roamcore_contract_power_current",
"source_pattern": "sensor\\.rc_power_battery_current",
"canonical_capability": "rc_power_battery_current",
"weight": 100,
"description": "Self-mapping of an existing canonical tile"
},
{
"id": "roamcore_contract_solar_power",
"source_pattern": "sensor\\.rc_power_solar_power",
"canonical_capability": "rc_power_solar_power",
"weight": 100,
"description": "Self-mapping of an existing canonical tile"
},
{
"id": "roamcore_contract_shore_connected",
"source_pattern": "binary_sensor\\.rc_power_shore_connected",
"canonical_capability": "rc_power_shore_connected",
"weight": 100,
"description": "Self-mapping of an existing canonical tile"
},
{
"id": "roamcore_contract_lighting_interior",
"source_pattern": "switch\\.rc_lighting_interior_state",
"canonical_capability": "rc_lighting_interior_state",
"weight": 100,
"description": "Self-mapping of an existing canonical tile"
},
{
"id": "roamcore_contract_lighting_approach",
"source_pattern": "switch\\.rc_lighting_approach_state",
"canonical_capability": "rc_lighting_approach_state",
"weight": 100,
"description": "Self-mapping of an existing canonical tile"
},
{
"id": "roamcore_contract_climate_indoor",
"source_pattern": "sensor\\.rc_climate_indoor_temperature",
"canonical_capability": "rc_climate_indoor_temperature",
"weight": 100,
"description": "Self-mapping of an existing canonical tile"
},
{
"id": "roamcore_contract_climate_hvac",
"source_pattern": "switch\\.rc_climate_hvac_state",
"canonical_capability": "rc_climate_hvac_state",
"weight": 100,
"description": "Self-mapping of an existing canonical tile"
},
{
"id": "roamcore_contract_water_fresh",
"source_pattern": "sensor\\.rc_water_fresh_level",
"canonical_capability": "rc_water_fresh_level",
"weight": 100,
"description": "Self-mapping of an existing canonical tile"
},
{
"id": "roamcore_contract_water_pump",
"source_pattern": "switch\\.rc_water_pump_state",
"canonical_capability": "rc_water_pump_state",
"weight": 100,
"description": "Self-mapping of an existing canonical tile"
},
{
"id": "roamcore_contract_position_lat",
"source_pattern": "(sensor|device_tracker)\\.rc_position_lat",
"canonical_capability": "rc_position_lat",
"weight": 100,
"description": "Self-mapping of an existing canonical tile"
},
{
"id": "roamcore_contract_position_lon",
"source_pattern": "(sensor|device_tracker)\\.rc_position_lon",
"canonical_capability": "rc_position_lon",
"weight": 100,
"description": "Self-mapping of an existing canonical tile"
},
{
"id": "roamcore_contract_network_internet",
"source_pattern": "binary_sensor\\.rc_network_internet_reachable",
"canonical_capability": "rc_network_internet_reachable",
"weight": 100,
"description": "Self-mapping of an existing canonical tile"
},
{
"id": "roamcore_contract_network_wan_ip",
"source_pattern": "sensor\\.rc_network_wan_ip",
"canonical_capability": "rc_network_wan_ip",
"weight": 100,
"description": "Self-mapping of an existing canonical tile"
},
{
"id": "generic_battery_soc_fallback",
"source_pattern": "sensor\\..*battery.*(soc|state_of_charge)",
"canonical_capability": "rc_power_battery_soc",
"weight": 60,
"description": "Last-resort fallback: any sensor mentioning battery + soc maps to the leisure battery percentage"
},
{
"id": "generic_solar_power_fallback",
"source_pattern": "sensor\\..*solar.*(power|w)",
"canonical_capability": "rc_power_solar_power",
"weight": 60,
"description": "Last-resort fallback: any sensor mentioning solar + power/w maps to solar charging power"
}
]
}
53 changes: 53 additions & 0 deletions docs/reference/rc-capability-mapping.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# How RoamCore knows where each device belongs

> **One sentence:** RoamCore automatically organises whatever you plug in — battery, solar, water, network — without you telling it which is which.

This page is for anyone who wants to understand *how* a new device ends up in the right place on the RoamCore dashboard, and what to do if it doesn't.

If you only want to install a device and see it on your dashboard, the connection wizard does all of this for you. You don't need to read this page. If you're curious, or you want to know why a Victron SmartShunt and a Renogy battery monitor both show up in the same `Battery` tile, keep reading.

---

## §1 What this is

Every device in your van — Victron, Renogy, a generic USB temperature sensor, anything — has its own brand-specific way of talking. Without help, your dashboard would need one card per brand, and switching brands would mean rebuilding the dashboard. RoamCore solves this with a **shared vocabulary** of "things in a van" — battery, solar, water, network, position, lighting — and a smart layer that quietly translates whatever you plug in into that vocabulary.

You do nothing. RoamCore looks at the device, decides which box on the dashboard it belongs in, and puts it there.

---

## §2 What you see

When you open the RoamCore dashboard, you see one **tile** per thing in the van. The tile is labelled in plain English (Battery, Solar, Fresh Water, Internet, GPS) and shows you the current value.

When you plug in a new device, the right tile just *updates with the new value*. You don't have to drag anything, you don't have to edit a YAML, you don't have to pick from a dropdown. The tile was already there — now it's showing real data from the new device.

If a device doesn't fit any known tile, RoamCore doesn't drop it on the floor. It goes into a "things we haven't categorised yet" panel in Advanced mode and stays out of your way.

---

## §3 What you do

**Nothing.** This is automatic.

If you're the curious type and want to peek under the hood: there is a small list of rules that tell RoamCore "an entity that mentions battery + voltage is the leisure battery voltage." That list lives in the RoamCore repo (it's plain text — you can read it in your browser). You don't need to edit it, and RoamCore will never ask you to. It's there so that power users can suggest new mappings if a new device appears that RoamCore doesn't recognise yet.

---

## §4 What to do if it goes wrong

If a device shows up in the wrong tile (for example, your water pump is showing up under "Lighting"), or if a device doesn't show up at all:

1. Open the **About this tile** panel on the tile that's wrong. It tells you which rule RoamCore used to decide where the device belongs.
2. If the rule is obviously wrong (e.g. the rule says "anything with `pump` in the name is a light"), file an issue against the RoamCore repo with the entity name from the panel. RoamCore will add a better rule and the next update will fix it for everyone.
3. If the device doesn't show up at all, the rule list probably doesn't have an entry for it yet. File the same kind of issue with the entity name — RoamCore's maintainers will add a rule.

In either case the message is the same: **file an issue with the entity name, RoamCore will fix it in a future update.** You don't need to edit anything yourself.

---

## §5 Useful links

- **The canonical tile list** — the full vocabulary of "things in a van" that RoamCore knows about. It's plain English and lives in the RoamCore repo.
- **The naming convention** — explains why every tile is labelled the way it is (so the dashboard stays stable when you switch brands).
- **The connection wizard** — the setup flow that adds a new device. It runs the same smart layer automatically; you don't need to know any of this to use it.
Loading