UNPKG

matterbridge-roborock-vacuum-plugin

Version:
198 lines 8.38 kB
import { randomInt } from 'node:crypto'; import { debugStringify } from 'matterbridge/logger'; import { CommonAreaNamespaceTag } from 'matterbridge/matter'; import { DEFAULT_AREA_ID_UNKNOWN, RANDOM_ROOM_MAX, RANDOM_ROOM_MIN } from '../constants/index.js'; import { RoomIndexMap, RoomMap } from '../core/application/models/index.js'; import { HomeEntity } from '../core/domain/entities/Home.js'; /** * Create a fallback service area for error cases. * @param areaId - Unique identifier for the area * @param reason - Reason for the fallback area creation * @returns Fallback service area configuration */ function createFallbackArea(areaId, reason) { return { areaId, mapId: 0, areaInfo: { locationInfo: { locationName: `Unknown - ${reason}`, floorNumber: 0, areaType: null, }, landmarkInfo: null, }, }; } export function toSupportedMaps(mapInfo, enableMultipleMap = true) { const maps = enableMultipleMap ? mapInfo.maps : mapInfo.maps.slice(0, 1); return maps.map((map) => ({ mapId: map.id, name: map.name ?? `Map ${map.id}`, })); } /** * Build placeholder supportedMaps entries for areas with non-null mapIds when no real maps are available. * Extracts distinct mapIds from areas and creates a synthetic map entry for each. * @param areas - Service areas to extract distinct mapIds from * @returns Array of placeholder map entries, one per distinct non-null mapId found */ function buildPlaceholderSupportedMaps(areas) { const distinctMapIds = [...new Set(areas.map((a) => a.mapId).filter((id) => id !== null))]; return distinctMapIds.map((mapId) => ({ mapId, name: `Map ${mapId}` })); } /** * Convert vacuum rooms and room map to Matter ServiceArea areas. * Handles single and multiple map configurations. * @param homeInFo - Home entity containing room and map information * @param logger - Logger for debugging and error reporting * @param enableMultipleMap - When false, expose only the primary map and its rooms * @returns Supported areas, maps, and room index mapping */ export function getSupportedAreas(homeInFo, logger, enableMultipleMap = true) { logger.debug('getSupportedAreas-vacuum room', debugStringify(homeInFo.rawRooms)); logger.debug('getSupportedAreas-roomMap', homeInFo.roomMap ? debugStringify(homeInFo.roomMap) : 'undefined'); const noVacuumRooms = !homeInFo.rawRooms || homeInFo.rawRooms.length === 0; const noRoomMap = !homeInFo.roomMap?.rooms || homeInFo.roomMap.rooms.length === 0; if (noVacuumRooms || noRoomMap) { if (noVacuumRooms) { logger.error('No rooms found'); } if (noRoomMap) { logger.error('No room map found'); } return { supportedAreas: [createFallbackArea(DEFAULT_AREA_ID_UNKNOWN, 'No Room')], supportedMaps: [{ mapId: 0, name: 'Default Map' }], roomIndexMap: new RoomIndexMap(new Map([[DEFAULT_AREA_ID_UNKNOWN, { roomId: DEFAULT_AREA_ID_UNKNOWN, mapId: 0, roomName: 'No Room' }]]), // areaInfo new Map([[`${DEFAULT_AREA_ID_UNKNOWN}-0`, { areaId: DEFAULT_AREA_ID_UNKNOWN, mapId: 0, roomName: 'No Room' }]])), }; } const entityForProcessing = enableMultipleMap ? homeInFo : new HomeEntity(homeInFo.id, homeInFo.name, new RoomMap(homeInFo.roomMap.getRooms(homeInFo.mapInfo.maps, false)), homeInFo.mapInfo, homeInFo.activeMapId); const { supportedAreas, areaInfos, roomInfos } = processValidData(entityForProcessing); const supportedMaps = toSupportedMaps(homeInFo.mapInfo, enableMultipleMap); const effectiveSupportedMaps = supportedMaps.length > 0 ? supportedMaps : buildPlaceholderSupportedMaps(supportedAreas); logger.debug('getSupportedAreas - supportedAreas', debugStringify(supportedAreas)); logger.debug('getSupportedAreas - supportedMaps', debugStringify(effectiveSupportedMaps)); const roomIndexMap = new RoomIndexMap(areaInfos, roomInfos); return { supportedAreas, supportedMaps: effectiveSupportedMaps, roomIndexMap, }; } function processValidData(homeInFo) { const areaInfos = new Map(); const roomInfos = new Map(); const supportedAreas = homeInFo.rawRooms.map((room, index) => { const locationName = room.iot_name ?? homeInFo.rawRooms.find((r) => String(r.id) === room.iot_name_id || r.id === room.id)?.iot_name ?? `Unknown Room ${randomInt(RANDOM_ROOM_MIN, RANDOM_ROOM_MAX)}`; const mapId = room.iot_map_id; areaInfos.set(index, { roomId: room.id, mapId: mapId, roomName: locationName }); roomInfos.set(`${room.id}-${room.iot_map_id}`, { areaId: index, mapId: room.iot_map_id, roomName: locationName }); return { areaId: index, mapId: mapId, areaInfo: { locationInfo: { locationName: locationName, floorNumber: mapId, areaType: populateAreaNamespaceTag(room), }, landmarkInfo: null, }, }; }); return { supportedAreas, areaInfos, roomInfos, }; } /** * Maps a B01 roomTypeId (0–11, per ioBroker ROOM_TYPE_ID_TO_TOKEN) to a CommonAreaNamespaceTag numeric value. * Returns null for unknown / fallback (roomTypeId 0). */ export function roomTypeIdToAreaTag(roomTypeId) { switch (roomTypeId) { case 0: return null; case 1: return CommonAreaNamespaceTag.PrimaryBedroom.tag; case 2: return CommonAreaNamespaceTag.GuestBedroom.tag; case 3: return CommonAreaNamespaceTag.Bedroom.tag; case 4: return CommonAreaNamespaceTag.LivingRoom.tag; case 5: return CommonAreaNamespaceTag.Dining.tag; case 6: return CommonAreaNamespaceTag.Kitchen.tag; case 7: return CommonAreaNamespaceTag.Balcony.tag; case 8: return CommonAreaNamespaceTag.Bathroom.tag; case 9: return CommonAreaNamespaceTag.Hallway.tag; case 10: return CommonAreaNamespaceTag.Study.tag; case 11: return CommonAreaNamespaceTag.Corridor.tag; // B01 extended room type IDs (2001–2011) — ioBroker ROOM_TYPE_MAP b01/constants.ts case 2001: return CommonAreaNamespaceTag.Bedroom.tag; // "bedroom" case 2002: return CommonAreaNamespaceTag.Dining.tag; // "dinnerroom" case 2003: return CommonAreaNamespaceTag.Bathroom.tag; // "restroom" case 2004: return CommonAreaNamespaceTag.Corridor.tag; // "corridor" case 2005: return CommonAreaNamespaceTag.Kitchen.tag; // "kitchen" case 2006: return CommonAreaNamespaceTag.LivingRoom.tag; // "livingroom" case 2007: return CommonAreaNamespaceTag.Balcony.tag; // "balcony" case 2008: return CommonAreaNamespaceTag.Study.tag; // "study" case 2009: return CommonAreaNamespaceTag.Hallway.tag; // "entryway" case 2010: return CommonAreaNamespaceTag.PrimaryBedroom.tag; // "masterbedrroom" case 2011: return CommonAreaNamespaceTag.GuestBedroom.tag; // "guestbedrroom" default: return null; } } function populateAreaNamespaceTag(room) { if (room.areaType !== undefined) { return room.areaType; } if (room.tag && room.tag > 0) { switch (room.tag) { case 1: return CommonAreaNamespaceTag.Bedroom.tag; case 2: return CommonAreaNamespaceTag.PrimaryBedroom.tag; case 3: return CommonAreaNamespaceTag.GuestBedroom.tag; case 6: return CommonAreaNamespaceTag.LivingRoom.tag; case 7: return CommonAreaNamespaceTag.Balcony.tag; case 9: return CommonAreaNamespaceTag.Study.tag; case 14: return CommonAreaNamespaceTag.Kitchen.tag; default: return null; } } return null; } //# sourceMappingURL=getSupportedAreas.js.map