881 lines
29 KiB
JSON
881 lines
29 KiB
JSON
[
|
|
{
|
|
"id": "6dfa989bb120e6c6",
|
|
"type": "tab",
|
|
"label": "Home Presence"
|
|
},
|
|
{
|
|
"id": "pres_c_homemode",
|
|
"type": "comment",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "── Home Mode ──",
|
|
"wires": [],
|
|
"info": "",
|
|
"x": 130,
|
|
"y": 60
|
|
},
|
|
{
|
|
"id": "pres001a2b3c4d5e",
|
|
"type": "server-state-changed",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Mischa presence",
|
|
"wires": [
|
|
[
|
|
"pres002a2b3c4d5e"
|
|
]
|
|
],
|
|
"server": "657c7463.48e1e4",
|
|
"version": 6,
|
|
"outputs": 1,
|
|
"exposeAsEntityConfig": "",
|
|
"entities": {
|
|
"entity": [
|
|
"person.mischa_gorinskat"
|
|
],
|
|
"substring": [],
|
|
"regex": []
|
|
},
|
|
"outputInitially": false,
|
|
"stateType": "str",
|
|
"ifState": "",
|
|
"ifStateType": "str",
|
|
"ifStateOperator": "is",
|
|
"outputOnlyOnStateChange": true,
|
|
"for": 0,
|
|
"forType": "num",
|
|
"forUnits": "minutes",
|
|
"ignorePrevStateNull": false,
|
|
"ignorePrevStateUnknown": false,
|
|
"ignorePrevStateUnavailable": false,
|
|
"ignoreCurrentStateUnknown": true,
|
|
"ignoreCurrentStateUnavailable": true,
|
|
"outputProperties": [
|
|
{
|
|
"property": "payload",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "entityState"
|
|
},
|
|
{
|
|
"property": "data",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "eventData"
|
|
},
|
|
{
|
|
"property": "topic",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "triggerId"
|
|
}
|
|
],
|
|
"x": 120,
|
|
"y": 100
|
|
},
|
|
{
|
|
"id": "pres002a2b3c4d5e",
|
|
"type": "switch",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Home or Away?",
|
|
"wires": [
|
|
[
|
|
"pres003a2b3c4d5e"
|
|
],
|
|
[
|
|
"pres004a2b3c4d5e"
|
|
]
|
|
],
|
|
"property": "payload",
|
|
"propertyType": "msg",
|
|
"rules": [
|
|
{
|
|
"t": "eq",
|
|
"v": "home",
|
|
"vt": "str"
|
|
},
|
|
{
|
|
"t": "eq",
|
|
"v": "not_home",
|
|
"vt": "str"
|
|
}
|
|
],
|
|
"checkall": "true",
|
|
"repair": false,
|
|
"outputs": 2,
|
|
"x": 400,
|
|
"y": 100
|
|
},
|
|
{
|
|
"id": "pres003a2b3c4d5e",
|
|
"type": "api-call-service",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Set Home",
|
|
"wires": [
|
|
[]
|
|
],
|
|
"server": "657c7463.48e1e4",
|
|
"version": 7,
|
|
"debugenabled": false,
|
|
"action": "input_select.select_option",
|
|
"floorId": [],
|
|
"areaId": [],
|
|
"deviceId": [],
|
|
"entityId": [
|
|
"input_select.home_mode"
|
|
],
|
|
"labelId": [],
|
|
"data": "{\"option\": \"Home\"}",
|
|
"dataType": "json",
|
|
"mergeContext": "",
|
|
"mustacheAltTags": false,
|
|
"outputProperties": [],
|
|
"queue": "none",
|
|
"blockInputOverrides": false,
|
|
"domain": "input_select",
|
|
"service": "select_option",
|
|
"x": 660,
|
|
"y": 60
|
|
},
|
|
{
|
|
"id": "pres004a2b3c4d5e",
|
|
"type": "api-call-service",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Set Away",
|
|
"wires": [
|
|
[]
|
|
],
|
|
"server": "657c7463.48e1e4",
|
|
"version": 7,
|
|
"debugenabled": false,
|
|
"action": "input_select.select_option",
|
|
"floorId": [],
|
|
"areaId": [],
|
|
"deviceId": [],
|
|
"entityId": [
|
|
"input_select.home_mode"
|
|
],
|
|
"labelId": [],
|
|
"data": "{\"option\": \"Away\"}",
|
|
"dataType": "json",
|
|
"mergeContext": "",
|
|
"mustacheAltTags": false,
|
|
"outputProperties": [],
|
|
"queue": "none",
|
|
"blockInputOverrides": false,
|
|
"domain": "input_select",
|
|
"service": "select_option",
|
|
"x": 660,
|
|
"y": 140
|
|
},
|
|
{
|
|
"id": "pres_sm_c_header",
|
|
"type": "comment",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "── Room Presence State Machine ──",
|
|
"wires": [],
|
|
"info": "",
|
|
"x": 190,
|
|
"y": 300
|
|
},
|
|
{
|
|
"id": "pres_sm_c_person",
|
|
"type": "comment",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Person",
|
|
"wires": [],
|
|
"info": "",
|
|
"x": 90,
|
|
"y": 360
|
|
},
|
|
{
|
|
"id": "pres_sm_c_hall",
|
|
"type": "comment",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Hallway (direction + state)",
|
|
"wires": [],
|
|
"info": "",
|
|
"x": 150,
|
|
"y": 480
|
|
},
|
|
{
|
|
"id": "pres_sm_c_rooms",
|
|
"type": "comment",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Room Motion Sensors",
|
|
"wires": [],
|
|
"info": "",
|
|
"x": 140,
|
|
"y": 780
|
|
},
|
|
{
|
|
"id": "pres_sm_c_sm",
|
|
"type": "comment",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "State Machine",
|
|
"wires": [],
|
|
"info": "",
|
|
"x": 450,
|
|
"y": 580
|
|
},
|
|
{
|
|
"id": "pres_sm_c_out",
|
|
"type": "comment",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Outputs",
|
|
"wires": [],
|
|
"info": "",
|
|
"x": 730,
|
|
"y": 580
|
|
},
|
|
{
|
|
"id": "pres_sm_person",
|
|
"type": "server-state-changed",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Person (room SM)",
|
|
"wires": [
|
|
[
|
|
"pres_sm_func"
|
|
]
|
|
],
|
|
"server": "657c7463.48e1e4",
|
|
"version": 6,
|
|
"outputs": 1,
|
|
"exposeAsEntityConfig": "",
|
|
"entities": {
|
|
"entity": [
|
|
"person.mischa_gorinskat"
|
|
],
|
|
"substring": [],
|
|
"regex": []
|
|
},
|
|
"outputInitially": false,
|
|
"stateType": "str",
|
|
"ifState": "",
|
|
"ifStateType": "str",
|
|
"ifStateOperator": "is",
|
|
"outputOnlyOnStateChange": true,
|
|
"for": 0,
|
|
"forType": "num",
|
|
"forUnits": "minutes",
|
|
"ignorePrevStateNull": false,
|
|
"ignorePrevStateUnknown": false,
|
|
"ignorePrevStateUnavailable": false,
|
|
"ignoreCurrentStateUnknown": true,
|
|
"ignoreCurrentStateUnavailable": true,
|
|
"outputProperties": [
|
|
{
|
|
"property": "payload",
|
|
"propertyType": "msg",
|
|
"value": "string",
|
|
"valueType": "entityState"
|
|
},
|
|
{
|
|
"property": "data",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "eventData"
|
|
},
|
|
{
|
|
"property": "topic",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "triggerId"
|
|
}
|
|
],
|
|
"x": 130,
|
|
"y": 400
|
|
},
|
|
{
|
|
"id": "pres_sm_hall1",
|
|
"type": "server-state-changed",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Hallway S1 (corner/front door)",
|
|
"wires": [
|
|
[
|
|
"pres_sm_func"
|
|
]
|
|
],
|
|
"server": "657c7463.48e1e4",
|
|
"version": 6,
|
|
"outputs": 1,
|
|
"exposeAsEntityConfig": "",
|
|
"entities": {
|
|
"entity": [
|
|
"binary_sensor.hallway_motion_sensor_1_occupancy"
|
|
],
|
|
"substring": [],
|
|
"regex": []
|
|
},
|
|
"outputInitially": false,
|
|
"stateType": "str",
|
|
"ifState": "",
|
|
"ifStateType": "str",
|
|
"ifStateOperator": "is",
|
|
"outputOnlyOnStateChange": true,
|
|
"for": 0,
|
|
"forType": "num",
|
|
"forUnits": "minutes",
|
|
"ignorePrevStateNull": false,
|
|
"ignorePrevStateUnknown": false,
|
|
"ignorePrevStateUnavailable": false,
|
|
"ignoreCurrentStateUnknown": true,
|
|
"ignoreCurrentStateUnavailable": true,
|
|
"outputProperties": [
|
|
{
|
|
"property": "payload",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "entityState"
|
|
},
|
|
{
|
|
"property": "data",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "eventData"
|
|
},
|
|
{
|
|
"property": "topic",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "triggerId"
|
|
}
|
|
],
|
|
"x": 160,
|
|
"y": 520
|
|
},
|
|
{
|
|
"id": "pres_sm_hall2",
|
|
"type": "server-state-changed",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Hallway S2 (near living room)",
|
|
"wires": [
|
|
[
|
|
"pres_sm_func"
|
|
]
|
|
],
|
|
"server": "657c7463.48e1e4",
|
|
"version": 6,
|
|
"outputs": 1,
|
|
"exposeAsEntityConfig": "",
|
|
"entities": {
|
|
"entity": [
|
|
"binary_sensor.hallway_motion_sensor_2_occupancy"
|
|
],
|
|
"substring": [],
|
|
"regex": []
|
|
},
|
|
"outputInitially": false,
|
|
"stateType": "str",
|
|
"ifState": "",
|
|
"ifStateType": "str",
|
|
"ifStateOperator": "is",
|
|
"outputOnlyOnStateChange": true,
|
|
"for": 0,
|
|
"forType": "num",
|
|
"forUnits": "minutes",
|
|
"ignorePrevStateNull": false,
|
|
"ignorePrevStateUnknown": false,
|
|
"ignorePrevStateUnavailable": false,
|
|
"ignoreCurrentStateUnknown": true,
|
|
"ignoreCurrentStateUnavailable": true,
|
|
"outputProperties": [
|
|
{
|
|
"property": "payload",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "entityState"
|
|
},
|
|
{
|
|
"property": "data",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "eventData"
|
|
},
|
|
{
|
|
"property": "topic",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "triggerId"
|
|
}
|
|
],
|
|
"x": 160,
|
|
"y": 580
|
|
},
|
|
{
|
|
"id": "pres_sm_hall3",
|
|
"type": "server-state-changed",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Hallway S3 (near kitchen)",
|
|
"wires": [
|
|
[
|
|
"pres_sm_func"
|
|
]
|
|
],
|
|
"server": "657c7463.48e1e4",
|
|
"version": 6,
|
|
"outputs": 1,
|
|
"exposeAsEntityConfig": "",
|
|
"entities": {
|
|
"entity": [
|
|
"binary_sensor.hallway_motion_sensor_3_occupancy"
|
|
],
|
|
"substring": [],
|
|
"regex": []
|
|
},
|
|
"outputInitially": false,
|
|
"stateType": "str",
|
|
"ifState": "",
|
|
"ifStateType": "str",
|
|
"ifStateOperator": "is",
|
|
"outputOnlyOnStateChange": true,
|
|
"for": 0,
|
|
"forType": "num",
|
|
"forUnits": "minutes",
|
|
"ignorePrevStateNull": false,
|
|
"ignorePrevStateUnknown": false,
|
|
"ignorePrevStateUnavailable": false,
|
|
"ignoreCurrentStateUnknown": true,
|
|
"ignoreCurrentStateUnavailable": true,
|
|
"outputProperties": [
|
|
{
|
|
"property": "payload",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "entityState"
|
|
},
|
|
{
|
|
"property": "data",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "eventData"
|
|
},
|
|
{
|
|
"property": "topic",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "triggerId"
|
|
}
|
|
],
|
|
"x": 150,
|
|
"y": 640
|
|
},
|
|
{
|
|
"id": "pres_sm_hallg",
|
|
"type": "server-state-changed",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Hallway (group)",
|
|
"wires": [
|
|
[
|
|
"pres_sm_func"
|
|
]
|
|
],
|
|
"server": "657c7463.48e1e4",
|
|
"version": 6,
|
|
"outputs": 1,
|
|
"exposeAsEntityConfig": "",
|
|
"entities": {
|
|
"entity": [
|
|
"binary_sensor.hallway_occupancy"
|
|
],
|
|
"substring": [],
|
|
"regex": []
|
|
},
|
|
"outputInitially": false,
|
|
"stateType": "str",
|
|
"ifState": "",
|
|
"ifStateType": "str",
|
|
"ifStateOperator": "is",
|
|
"outputOnlyOnStateChange": true,
|
|
"for": 0,
|
|
"forType": "num",
|
|
"forUnits": "minutes",
|
|
"ignorePrevStateNull": false,
|
|
"ignorePrevStateUnknown": false,
|
|
"ignorePrevStateUnavailable": false,
|
|
"ignoreCurrentStateUnknown": true,
|
|
"ignoreCurrentStateUnavailable": true,
|
|
"outputProperties": [
|
|
{
|
|
"property": "payload",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "entityState"
|
|
},
|
|
{
|
|
"property": "data",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "eventData"
|
|
},
|
|
{
|
|
"property": "topic",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "triggerId"
|
|
}
|
|
],
|
|
"x": 120,
|
|
"y": 700
|
|
},
|
|
{
|
|
"id": "pres_sm_lr",
|
|
"type": "server-state-changed",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Living Room Motion",
|
|
"wires": [
|
|
[
|
|
"pres_sm_func"
|
|
]
|
|
],
|
|
"server": "657c7463.48e1e4",
|
|
"version": 6,
|
|
"outputs": 1,
|
|
"exposeAsEntityConfig": "",
|
|
"entities": {
|
|
"entity": [
|
|
"binary_sensor.livingroom_motion_sensor_occupancy"
|
|
],
|
|
"substring": [],
|
|
"regex": []
|
|
},
|
|
"outputInitially": false,
|
|
"stateType": "str",
|
|
"ifState": "",
|
|
"ifStateType": "str",
|
|
"ifStateOperator": "is",
|
|
"outputOnlyOnStateChange": true,
|
|
"for": 0,
|
|
"forType": "num",
|
|
"forUnits": "minutes",
|
|
"ignorePrevStateNull": false,
|
|
"ignorePrevStateUnknown": false,
|
|
"ignorePrevStateUnavailable": false,
|
|
"ignoreCurrentStateUnknown": true,
|
|
"ignoreCurrentStateUnavailable": true,
|
|
"outputProperties": [
|
|
{
|
|
"property": "payload",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "entityState"
|
|
},
|
|
{
|
|
"property": "data",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "eventData"
|
|
},
|
|
{
|
|
"property": "topic",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "triggerId"
|
|
}
|
|
],
|
|
"x": 130,
|
|
"y": 820
|
|
},
|
|
{
|
|
"id": "pres_sm_bed",
|
|
"type": "server-state-changed",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Bedroom Motion",
|
|
"wires": [
|
|
[
|
|
"pres_sm_func"
|
|
]
|
|
],
|
|
"server": "657c7463.48e1e4",
|
|
"version": 6,
|
|
"outputs": 1,
|
|
"exposeAsEntityConfig": "",
|
|
"entities": {
|
|
"entity": [
|
|
"binary_sensor.bedroom_motion_sensor_occupancy"
|
|
],
|
|
"substring": [],
|
|
"regex": []
|
|
},
|
|
"outputInitially": false,
|
|
"stateType": "str",
|
|
"ifState": "",
|
|
"ifStateType": "str",
|
|
"ifStateOperator": "is",
|
|
"outputOnlyOnStateChange": true,
|
|
"for": 0,
|
|
"forType": "num",
|
|
"forUnits": "minutes",
|
|
"ignorePrevStateNull": false,
|
|
"ignorePrevStateUnknown": false,
|
|
"ignorePrevStateUnavailable": false,
|
|
"ignoreCurrentStateUnknown": true,
|
|
"ignoreCurrentStateUnavailable": true,
|
|
"outputProperties": [
|
|
{
|
|
"property": "payload",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "entityState"
|
|
},
|
|
{
|
|
"property": "data",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "eventData"
|
|
},
|
|
{
|
|
"property": "topic",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "triggerId"
|
|
}
|
|
],
|
|
"x": 120,
|
|
"y": 880
|
|
},
|
|
{
|
|
"id": "pres_sm_bath",
|
|
"type": "server-state-changed",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Bathroom Motion",
|
|
"wires": [
|
|
[
|
|
"pres_sm_func"
|
|
]
|
|
],
|
|
"server": "657c7463.48e1e4",
|
|
"version": 6,
|
|
"outputs": 1,
|
|
"exposeAsEntityConfig": "",
|
|
"entities": {
|
|
"entity": [
|
|
"binary_sensor.bathroom_motion_occupancy"
|
|
],
|
|
"substring": [],
|
|
"regex": []
|
|
},
|
|
"outputInitially": false,
|
|
"stateType": "str",
|
|
"ifState": "",
|
|
"ifStateType": "str",
|
|
"ifStateOperator": "is",
|
|
"outputOnlyOnStateChange": true,
|
|
"for": 0,
|
|
"forType": "num",
|
|
"forUnits": "minutes",
|
|
"ignorePrevStateNull": false,
|
|
"ignorePrevStateUnknown": false,
|
|
"ignorePrevStateUnavailable": false,
|
|
"ignoreCurrentStateUnknown": true,
|
|
"ignoreCurrentStateUnavailable": true,
|
|
"outputProperties": [
|
|
{
|
|
"property": "payload",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "entityState"
|
|
},
|
|
{
|
|
"property": "data",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "eventData"
|
|
},
|
|
{
|
|
"property": "topic",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "triggerId"
|
|
}
|
|
],
|
|
"x": 120,
|
|
"y": 940
|
|
},
|
|
{
|
|
"id": "pres_sm_kit",
|
|
"type": "server-state-changed",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Kitchen Motion",
|
|
"wires": [
|
|
[
|
|
"pres_sm_func"
|
|
]
|
|
],
|
|
"server": "657c7463.48e1e4",
|
|
"version": 6,
|
|
"outputs": 1,
|
|
"exposeAsEntityConfig": "",
|
|
"entities": {
|
|
"entity": [
|
|
"binary_sensor.kitchen_motion_sensor_occupancy"
|
|
],
|
|
"substring": [],
|
|
"regex": []
|
|
},
|
|
"outputInitially": false,
|
|
"stateType": "str",
|
|
"ifState": "",
|
|
"ifStateType": "str",
|
|
"ifStateOperator": "is",
|
|
"outputOnlyOnStateChange": true,
|
|
"for": 0,
|
|
"forType": "num",
|
|
"forUnits": "minutes",
|
|
"ignorePrevStateNull": false,
|
|
"ignorePrevStateUnknown": false,
|
|
"ignorePrevStateUnavailable": false,
|
|
"ignoreCurrentStateUnknown": true,
|
|
"ignoreCurrentStateUnavailable": true,
|
|
"outputProperties": [
|
|
{
|
|
"property": "payload",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "entityState"
|
|
},
|
|
{
|
|
"property": "data",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "eventData"
|
|
},
|
|
{
|
|
"property": "topic",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "triggerId"
|
|
}
|
|
],
|
|
"x": 120,
|
|
"y": 1000
|
|
},
|
|
{
|
|
"id": "pres_sm_func",
|
|
"type": "function",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Room Presence SM",
|
|
"wires": [
|
|
[
|
|
"pres_sm_api"
|
|
],
|
|
[
|
|
"pres_sm_dbg"
|
|
]
|
|
],
|
|
"func": "// === ROOM PRESENCE STATE MACHINE ===\n// msg.topic = triggering entity_id (from triggerId output property)\n// msg.payload = entity state string\n// msg.data = full HASC event data\n\nconst entityId = (msg.data && msg.data.entity_id) ? msg.data.entity_id : (msg.topic || '');\nconst newState = (msg.data && msg.data.new_state) ? msg.data.new_state.state : msg.payload;\n\n// Guest mode guard — multi-person breaks single-occupant assumptions\nconst haStates = (global.get('homeassistant') || {}).homeAssistantLatest\n ? global.get('homeassistant').homeAssistantLatest.states : {};\nconst guestMode = (haStates['input_boolean.guest_mode'] || {}).state;\nif (guestMode === 'on') return null;\n\n// Current state from flow context\nlet currentRoom = flow.get('currentRoom') || 'unknown';\n\n// ── Helpers ────────────────────────────────────────────────────────────────\n\nfunction capitalize(s) {\n const map = {\n unknown: 'Unknown', away: 'Away', hallway: 'Hallway',\n living_room: 'Livingroom', bedroom: 'Bedroom',\n bathroom: 'Bathroom', kitchen: 'Kitchen', balcony: 'Balcony'\n };\n return map[s] || (s.charAt(0).toUpperCase() + s.slice(1));\n}\n\nfunction cancelDestTimer() {\n const h = flow.get('destTimerHandle');\n if (h) { clearTimeout(h); flow.set('destTimerHandle', null); }\n}\n\nfunction cancelBalconyTimer() {\n const h = flow.get('balconyTimerHandle');\n if (h) { clearTimeout(h); flow.set('balconyTimerHandle', null); }\n}\n\nfunction setDestTimer() {\n cancelDestTimer();\n const h = setTimeout(function () {\n const lr = flow.get('lastRoom') || 'unknown';\n flow.set('currentRoom', lr);\n flow.set('destTimerHandle', null);\n node.send([\n { payload: capitalize(lr) },\n { payload: '[presence] dest-timer expired → reverted to ' + lr }\n ]);\n }, 90000);\n flow.set('destTimerHandle', h);\n}\n\nfunction setBalconyTimer() {\n cancelBalconyTimer();\n const h = setTimeout(function () {\n const cr = flow.get('currentRoom');\n if (cr === 'living_room') {\n flow.set('currentRoom', 'balcony');\n flow.set('balconyTimerHandle', null);\n node.send([\n { payload: 'Balcony' },\n { payload: '[presence] balcony timer fired → Balcony' }\n ]);\n }\n }, 900000); // 15 minutes\n flow.set('balconyTimerHandle', h);\n}\n\nfunction setState(newRoom) {\n const prev = flow.get('currentRoom') || 'unknown';\n if (newRoom === prev) return null; // no change needed\n flow.set('lastRoom', prev);\n flow.set('currentRoom', newRoom);\n return [\n { payload: capitalize(newRoom) },\n { payload: '[presence] ' + prev + ' → ' + newRoom + ' (via ' + entityId + ')' }\n ];\n}\n\nfunction updateHallwaySequence(sensorNum) {\n let seq = flow.get('hallwaySequence') || [];\n const now = Date.now();\n seq = seq.filter(function (e) { return now - e.time < 15000; });\n seq.push({ sensor: sensorNum, time: now });\n flow.set('hallwaySequence', seq);\n flow.set('lastHallwayTime', now);\n}\n\n// ── Transitions ────────────────────────────────────────────────────────────\n\n// 1. Person home / away\nif (entityId === 'person.mischa_gorinskat') {\n if (newState === 'not_home') {\n cancelDestTimer();\n cancelBalconyTimer();\n return setState('away');\n }\n if (newState === 'home') {\n return setState('unknown');\n }\n return null;\n}\n\n// Ignore all motion when person is away\nif (currentRoom === 'away') return null;\n\n// 2. Individual hallway sensors — direction tracking only, no state change\nif (entityId === 'binary_sensor.hallway_motion_sensor_1_occupancy') {\n if (newState === 'on') updateHallwaySequence(1);\n return null;\n}\nif (entityId === 'binary_sensor.hallway_motion_sensor_2_occupancy') {\n if (newState === 'on') updateHallwaySequence(2);\n return null;\n}\nif (entityId === 'binary_sensor.hallway_motion_sensor_3_occupancy') {\n if (newState === 'on') updateHallwaySequence(3);\n return null;\n}\n\n// 3. Hallway group — primary state change trigger\nif (entityId === 'binary_sensor.hallway_occupancy') {\n if (newState === 'on') {\n cancelBalconyTimer();\n flow.set('lastRoom', currentRoom); // save room before entering hallway\n setDestTimer(); // 90s: if no room fires → revert\n return setState('hallway');\n }\n return null; // hallway turning off doesn't trigger a state change\n}\n\n// 4. Room sensors\nconst roomMap = {\n 'binary_sensor.livingroom_motion_sensor_occupancy': 'living_room',\n 'binary_sensor.bedroom_motion_sensor_occupancy': 'bedroom',\n 'binary_sensor.bathroom_motion_occupancy': 'bathroom',\n 'binary_sensor.kitchen_motion_sensor_occupancy': 'kitchen',\n 'binary_sensor.balcony_motion_occupancy': 'balcony'\n};\n\nconst targetRoom = roomMap[entityId];\nif (targetRoom) {\n if (newState === 'on') {\n cancelDestTimer(); // confirmed destination\n cancelBalconyTimer();\n\n // Return from balcony via living room motion\n if (targetRoom === 'living_room' && currentRoom === 'balcony') {\n return setState('living_room');\n }\n // Accept if we came through hallway, are re-confirming same room, or state is unknown\n if (currentRoom === 'hallway' || currentRoom === targetRoom || currentRoom === 'unknown' ||\n (targetRoom === 'balcony' && currentRoom === 'living_room')) {\n return setState(targetRoom);\n }\n return null;\n }\n\n // LR motion off → start balcony detection (15 min no-hallway = balcony assumed)\n if (newState === 'off' && targetRoom === 'living_room' && currentRoom === 'living_room') {\n setBalconyTimer();\n }\n return null;\n}\n\nreturn null;",
|
|
"outputs": 2,
|
|
"timeout": 0,
|
|
"noerr": 0,
|
|
"initialize": "",
|
|
"finalize": "",
|
|
"libs": [],
|
|
"x": 480,
|
|
"y": 620
|
|
},
|
|
{
|
|
"id": "pres_sm_api",
|
|
"type": "api-call-service",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Set Room Presence",
|
|
"wires": [
|
|
[]
|
|
],
|
|
"server": "657c7463.48e1e4",
|
|
"version": 7,
|
|
"debugenabled": false,
|
|
"action": "input_select.select_option",
|
|
"floorId": [],
|
|
"areaId": [],
|
|
"deviceId": [],
|
|
"entityId": [
|
|
"input_select.room_presence"
|
|
],
|
|
"labelId": [],
|
|
"data": "{\"option\": \"{{{payload}}}\"}",
|
|
"dataType": "json",
|
|
"mergeContext": "",
|
|
"mustacheAltTags": false,
|
|
"outputProperties": [],
|
|
"queue": "none",
|
|
"blockInputOverrides": false,
|
|
"domain": "input_select",
|
|
"service": "select_option",
|
|
"x": 780,
|
|
"y": 620
|
|
},
|
|
{
|
|
"id": "pres_sm_dbg",
|
|
"type": "debug",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Presence log",
|
|
"wires": [],
|
|
"active": false,
|
|
"tosidebar": true,
|
|
"console": false,
|
|
"tostatus": false,
|
|
"complete": "payload",
|
|
"targetType": "msg",
|
|
"statusVal": "",
|
|
"statusType": "auto",
|
|
"x": 750,
|
|
"y": 680
|
|
},
|
|
{
|
|
"id": "pres_sm_balcony",
|
|
"type": "server-state-changed",
|
|
"z": "6dfa989bb120e6c6",
|
|
"name": "Balcony Motion",
|
|
"wires": [
|
|
[
|
|
"pres_sm_func"
|
|
]
|
|
],
|
|
"server": "657c7463.48e1e4",
|
|
"version": 6,
|
|
"outputs": 1,
|
|
"exposeAsEntityConfig": "",
|
|
"entities": {
|
|
"entity": [
|
|
"binary_sensor.balcony_motion_occupancy"
|
|
],
|
|
"substring": [],
|
|
"regex": []
|
|
},
|
|
"outputInitially": false,
|
|
"stateType": "str",
|
|
"ifState": "",
|
|
"ifStateType": "str",
|
|
"ifStateOperator": "is",
|
|
"outputOnlyOnStateChange": true,
|
|
"for": 0,
|
|
"forType": "num",
|
|
"forUnits": "minutes",
|
|
"ignorePrevStateNull": false,
|
|
"ignorePrevStateUnknown": false,
|
|
"ignorePrevStateUnavailable": false,
|
|
"ignoreCurrentStateUnknown": true,
|
|
"ignoreCurrentStateUnavailable": true,
|
|
"outputProperties": [
|
|
{
|
|
"property": "payload",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "entityState"
|
|
},
|
|
{
|
|
"property": "data",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "eventData"
|
|
},
|
|
{
|
|
"property": "topic",
|
|
"propertyType": "msg",
|
|
"value": "",
|
|
"valueType": "triggerId"
|
|
}
|
|
],
|
|
"x": 120,
|
|
"y": 1060
|
|
}
|
|
] |