changedInput schema / properties / edits / items / oneOf
Previous value: -[
- {
- "additionalProperties": false,
- "properties": {
- "direction_deg": {
- "description": "Compass direction the face looks to: 0 north, 90 east, 180 south, 270 west.",
- "maximum": 360,
- "minimum": 0,
- "type": "number"
- },
- "face": {
- "description": "A face id from the last answer's \"faces\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "tilt_deg": {
- "description": "Tilt, degrees (0 flat).",
- "maximum": 60,
- "minimum": 0,
- "type": "number"
- },
- "type": {
- "const": "slope",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "edge": {
- "description": "Which edge moves: the eave, or the left or right end seen from below.",
- "enum": [
- "eave",
- "left",
- "right"
- ],
- "type": "string"
- },
- "face": {
- "description": "A face id from the last answer's \"faces\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "metres": {
- "description": "Or a change: metres out from where the edge is now (negative: in), horizontal.",
- "maximum": 1.5,
- "minimum": -1.5,
- "type": "number"
- },
- "to_m": {
- "description": "Eave only: how far the eave should reach past the wall, horizontal metres (\"Dachüberstand 50 cm\" → 0.5). The face list shows the current eave_overhang_m.",
- "maximum": 1.5,
- "minimum": 0,
- "type": "number"
- },
- "type": {
- "const": "overhang",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face",
- "edge"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "face": {
- "description": "A face id from the last answer's \"faces\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "from_eave_m": {
- "description": "A cut along the eave, this far up from it.",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "from_left_m": {
- "description": "A cut up the slope, this far from the left end.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "type": {
- "const": "split",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "face": {
- "description": "A face id from the last answer's \"faces\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "remove_face",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "face": {
- "description": "A face id from the last answer's \"faces\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "restore_face",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "corners_m": {
- "description": "Any shape instead: its corners as [east, north] metres in the same frame as \"house.footprint_m\" (from a building plan).",
- "items": {
- "items": false,
- "maxItems": 2,
- "minItems": 2,
- "prefixItems": [
- {
- "type": "number"
- },
- {
- "type": "number"
- }
- ],
- "type": "array"
- },
- "maxItems": 24,
- "minItems": 3,
- "type": "array"
- },
- "depth_m": {
- "description": "Out from the wall, metres.",
- "maximum": 20,
- "minimum": 1,
- "type": "number"
- },
- "direction_deg": {
- "description": "Way the roof slopes down to (default: away from the wall).",
- "maximum": 360,
- "minimum": 0,
- "type": "number"
- },
- "eave_height_m": {
- "description": "Height of the roof's lowest edge above the ground, metres.",
- "maximum": 15,
- "minimum": 1.5,
- "type": "number"
- },
- "offset_m": {
- "description": "From the middle of the wall: positive to the east (north and south walls) or north (east and west walls).",
- "maximum": 30,
- "minimum": -30,
- "type": "number"
- },
- "tilt_deg": {
- "description": "Roof tilt, degrees (default 0, flat).",
- "maximum": 60,
- "minimum": 0,
- "type": "number"
- },
- "type": {
- "const": "add_face",
- "type": "string"
- },
- "wall": {
- "description": "The house wall the new part stands against (an extension, a garage, a porch). With width_m and depth_m; or give corners_m instead.",
- "enum": [
- "north",
- "east",
- "south",
- "west"
- ],
- "type": "string"
- },
- "width_m": {
- "description": "Along the wall, metres.",
- "maximum": 30,
- "minimum": 1,
- "type": "number"
- }
- },
- "required": [
- "type",
- "eave_height_m"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "depth_m": {
- "description": "Its depth up the roof, metres in the slope (a roof window \"78 × 140\": 1.4); a dormer's horizontally, as on a drawing from above. A ladder starts at from_eave_m and runs this far up; without it a ladder runs from the eave to the ridge.",
- "maximum": 12,
- "minimum": 0.1,
- "type": "number"
- },
- "face": {
- "description": "A face id from the last answer's \"faces\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "from_eave_m": {
- "description": "Horizontal metres up from the eave (as on a drawing seen from above).",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "from_left_m": {
- "description": "Metres from the face's left end, seen from below.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "height_m": {
- "description": "How high it stands above the roof, metres (a dormer: its front wall; default: the kind's usual height).",
- "maximum": 4,
- "minimum": 0.1,
- "type": "number"
- },
- "kind": {
- "description": "What it is: chimney, window, vent, flue, dish, antenna, ladder, snowWood, snowGrid, steps, solarThermal, pv, dome, dormerShed, dormerFlat, dormerGable, dormerTwin, dormerHip, dormerPoint, dormerTrapez, dormerBarrel, dormerBat, dormerWall, or block (anything else, 60 cm square).",
- "enum": [
- "chimney",
- "window",
- "vent",
- "flue",
- "dish",
- "antenna",
- "ladder",
- "snowWood",
- "snowGrid",
- "steps",
- "solarThermal",
- "pv",
- "dome",
- "dormerShed",
- "dormerFlat",
- "dormerGable",
- "dormerTwin",
- "dormerHip",
- "dormerPoint",
- "dormerTrapez",
- "dormerBarrel",
- "dormerBat",
- "dormerWall",
- "block"
- ],
- "type": "string"
- },
- "type": {
- "const": "add_obstacle",
- "type": "string"
- },
- "width_m": {
- "description": "Its width along the eave, metres in the roof's slope (a dormer, a roof window \"78 × 140\": 0.78). Default: the kind's usual size.",
- "maximum": 12,
- "minimum": 0.1,
- "type": "number"
- }
- },
- "required": [
- "type",
- "face",
- "kind",
- "from_eave_m",
- "from_left_m"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "depth_m": {
- "description": "A new depth up the roof, metres in the slope (a dormer's horizontally).",
- "maximum": 12,
- "minimum": 0.1,
- "type": "number"
- },
- "id": {
- "description": "An obstacle id from \"obstacles\" or \"your_obstacles\" (e.g. a dormer the scan found). A found one becomes one of your obstacles (u:<n>), but its id keeps working: later edits that name it (edit_obstacle, change_kind, fill_dormer, clear_dormer, find_similar, remove_obstacle, confirm_obstacle) and its dormer roofs (g:<id>:<n>) reach the new one, in this call and later ones, while it stays on the found one's spot; the answer's \"id_map\" lists them ({ found id: u:<n> }). A dormer's modules and roof settings move to its new roofs where they are the same, and the answer says what did not.",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "kind": {
- "description": "The new kind. Dormers: dormerShed, dormerFlat, dormerGable, dormerTwin, dormerHip, dormerPoint, dormerTrapez, dormerBarrel, dormerBat, dormerWall (a dormer stays its size). Or chimney, window, vent, flue, dish, antenna, ladder, snowWood, snowGrid, steps, solarThermal, pv, dome, block.",
- "enum": [
- "chimney",
- "window",
- "vent",
- "flue",
- "dish",
- "antenna",
- "ladder",
- "snowWood",
- "snowGrid",
- "steps",
- "solarThermal",
- "pv",
- "dome",
- "dormerShed",
- "dormerFlat",
- "dormerGable",
- "dormerTwin",
- "dormerHip",
- "dormerPoint",
- "dormerTrapez",
- "dormerBarrel",
- "dormerBat",
- "dormerWall",
- "block"
- ],
- "type": "string"
- },
- "type": {
- "const": "change_kind",
- "type": "string"
- },
- "width_m": {
- "description": "A new width along the eave, metres in the slope.",
- "maximum": 12,
- "minimum": 0.1,
- "type": "number"
- }
- },
- "required": [
- "type",
- "id",
- "kind"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "colour": {
- "description": "A colour id. metal: anthracite, aluminium, zinc, copper, brown; dormerBarrel: tiles, anthracite, aluminium, zinc, copper, brown; dormerShed: tiles, anthracite, aluminium, zinc, copper, brown; block: wall, grey, anthracite, red, brown; window: silver, anthracite, white, brown; chimney: render, brick, slate, steel, copper; flue: steel, anthracite, red; vent: anthracite, red, brown, grey; dish: light, anthracite, red; antenna: silver, anthracite; ladder: anthracite, red, brown, galvanised; snowWood: natural, dark, weathered; snowGrid: anthracite, red, brown, galvanised; steps: anthracite, red, brown, galvanised; solarThermal: silver, black; pv: silver, black; dome: opal, clear",
- "maxLength": 30,
- "minLength": 1,
- "type": "string"
- },
- "face": {
- "description": "With item \"metal\": only this face's sheet (a copper garage beside a zinc roof).",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "item": {
- "description": "What gets the colour: roof (a tiled roof, for the building part of face, the house without face; colours: photo, clay, red, darkRed, copper, brown, black, anthracite, slate, grey), metal (sheet-metal dormer tops and Falz- or Trapezblech roofs), or a kind of item.",
- "enum": [
- "roof",
- "metal",
- "dormerBarrel",
- "dormerShed",
- "block",
- "window",
- "chimney",
- "flue",
- "vent",
- "dish",
- "antenna",
- "ladder",
- "snowWood",
- "snowGrid",
- "steps",
- "solarThermal",
- "pv",
- "dome"
- ],
- "type": "string"
- },
- "type": {
- "const": "colour",
- "type": "string"
- }
- },
- "required": [
- "type",
- "item",
- "colour"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "covering": {
- "description": "The roof's covering: tiles, ton (Tonziegel, a narrow interlocking clay tile; mounted like tiles), biber (Biberschwanz plain tiles), flach (flat tiles such as Tegalit), falz (standing-seam sheet, modules flush) or trapez (trapezoidal sheet).",
- "enum": [
- "tiles",
- "ton",
- "biber",
- "flach",
- "falz",
- "trapez"
- ],
- "type": "string"
- },
- "face": {
- "description": "Only this face (a garage in Falzblech, say), from \"faces\" or \"dormer_roofs\"; without it the whole roof. On a flat dormer roof it decides the mounting: tiles (a plain flat roof) or trapez puts the modules on racks, falz lays them flush (without a choice: racks where it stands on a flat roof or measures 20 m² or more; see its \"mounting\").",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "flat_rack": {
- "description": "Racks on flat roofs: ew (east-west pairs back to back, default), south (rows facing south), ns (along the roof).",
- "enum": [
- "ew",
- "south",
- "ns"
- ],
- "type": "string"
- },
- "rack_tilt_deg": {
- "anyOf": [
- {
- "const": 5,
- "type": "number"
- },
- {
- "const": 10,
- "type": "number"
- },
- {
- "const": 15,
- "type": "number"
- }
- ],
- "description": "Tilt of the racks on flat roofs and racked trapezoidal sheet: 5, 10 (default) or 15 degrees."
- },
- "trapez_racks": {
- "description": "Trapezoidal sheet sloping 15° or less: modules on racks instead of flush.",
- "type": "boolean"
- },
- "type": {
- "const": "covering",
- "type": "string"
- }
- },
- "required": [
- "type"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Place an R × C field of modules exactly where the person's drawing shows it, in the face's own metres. Use dry_run first when unsure.",
- "properties": {
- "anchor": {
- "description": "What from_eave_m and from_left_m name: the field's lower left corner (default) or its middle.",
- "enum": [
- "lower_left",
- "centre"
- ],
- "type": "string"
- },
- "columns": {
- "description": "Modules side by side (default 1).",
- "maximum": 20,
- "minimum": 1,
- "type": "number"
- },
- "dry_run": {
- "description": "true: change nothing; the answer's \"dry_run\" lists the modules that would be placed (row, column, centre, orientation) and every collision with its reason.",
- "type": "boolean"
- },
- "exact": {
- "description": "Default true: all or nothing. If any module of the R × C field would collide (an obstacle, the edge margin, another module) nothing is placed and every collision is listed. false: place the ones that fit (as add_modules).",
- "type": "boolean"
- },
- "face": {
- "description": "A face id from \"faces\" or \"dormer_roofs\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "from_eave_m": {
- "description": "The field's anchor: horizontal metres up from the eave (the lower left corner, or the middle with anchor \"centre\").",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "from_left_m": {
- "description": "The anchor: metres from the face's left end, seen from below.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "orientation": {
- "description": "portrait: the module's long side up the slope (across the eave); landscape: long side along the eave. The same on every face, house roof or dormer roof: \"up the slope\" and \"along the eave\" are the face's own axes (geometry.faces[].slope_axis / eave_axis; on a flat face its longest edge). On racks every module stands landscape and this does not apply.",
- "enum": [
- "portrait",
- "landscape"
- ],
- "type": "string"
- },
- "rows": {
- "description": "Rows up the roof (default 1).",
- "maximum": 10,
- "minimum": 1,
- "type": "number"
- },
- "snap_to_grid": {
- "description": "true: the field's first module is moved onto the module grid of the nearest module on this face (same pitch, same rows and columns, its orientation unless given), so a new field lines up with an existing one. The answer says by how many cm it moved.",
- "type": "boolean"
- },
- "type": {
- "const": "place_field",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face",
- "from_eave_m",
- "from_left_m"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Continue a row (or column) of modules in a direction, on the row's own grid: the same pitch, orientation, type and line, one clamp gap on from its last module. A neighbouring field on the same grid is met exactly.",
- "properties": {
- "count": {
- "description": "How many modules to add. Without it: as many as fit, up to the next obstacle, module or edge (the answer says what stopped the row).",
- "maximum": 20,
- "minimum": 1,
- "type": "integer"
- },
- "direction": {
- "description": "Which way: right or left seen from below along the eave; up or down the slope.",
- "enum": [
- "right",
- "left",
- "up",
- "down"
- ],
- "type": "string"
- },
- "dry_run": {
- "description": "true: change nothing; \"dry_run\" lists what would be added.",
- "type": "boolean"
- },
- "face": {
- "description": "A face id from \"faces\" or \"dormer_roofs\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "from_eave_m": {
- "description": "A spot on a module of the row (or column) to continue.",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "from_left_m": {
- "description": "Metres from the face's left end, seen from below.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "type": {
- "const": "continue_row",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face",
- "from_eave_m",
- "from_left_m",
- "direction"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Move a dormer and keep its modules: the dormer moves as with edit_obstacle, its automatically laid modules follow, and the modules laid by hand on its roofs are carried the same distance.",
- "properties": {
- "id": {
- "description": "A dormer's obstacle id (\"obstacles\" or \"your_obstacles\"; a replaced dormer's old id works).",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "right_m": {
- "description": "Move it to the right seen from below (negative: left), metres.",
- "maximum": 10,
- "minimum": -10,
- "type": "number"
- },
- "type": {
- "const": "move_dormer",
- "type": "string"
- },
- "up_m": {
- "description": "Move it up the roof (negative: down), horizontal metres.",
- "maximum": 10,
- "minimum": -10,
- "type": "number"
- }
- },
- "required": [
- "type",
- "id"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "face": {
- "description": "A face id from \"faces\" or \"dormer_roofs\". Its modules stay exactly where they are from now on (the other module edits do this by themselves).",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "lay_by_hand",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "columns": {
- "description": "Modules side by side (default 1).",
- "maximum": 20,
- "minimum": 1,
- "type": "number"
- },
- "face": {
- "description": "A face id from \"faces\" or \"dormer_roofs\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "from_eave_m": {
- "description": "The field's lower left corner: horizontal metres up from the eave.",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "from_left_m": {
- "description": "The field's lower left corner: metres from the face's left end, seen from below.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "orientation": {
- "description": "Default portrait (landscape if the plan is set to landscape). portrait: the module's long side up the slope (across the eave); landscape: long side along the eave. The same on every face, house roof or dormer roof: \"up the slope\" and \"along the eave\" are the face's own axes (geometry.faces[].slope_axis / eave_axis; on a flat face its longest edge). On racks every module stands landscape and this does not apply.",
- "enum": [
- "portrait",
- "landscape"
- ],
- "type": "string"
- },
- "rows": {
- "description": "Rows up the roof (default 1).",
- "maximum": 10,
- "minimum": 1,
- "type": "number"
- },
- "type": {
- "const": "add_modules",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face",
- "from_eave_m",
- "from_left_m"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "face": {
- "description": "A face id from \"faces\" or \"dormer_roofs\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "from_eave_m": {
- "description": "A spot on the module to take off, or one corner of an area: horizontal metres up from the eave.",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "from_left_m": {
- "description": "Metres from the face's left end, seen from below.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "to_eave_m": {
- "description": "The area's other corner (every module whose centre lies inside goes).",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "to_left_m": {
- "description": "The area's other corner.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "type": {
- "const": "remove_modules",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face",
- "from_eave_m",
- "from_left_m"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Put modules taken out of the automatic layout back.",
- "properties": {
- "face": {
- "description": "A face id from \"faces\" or \"dormer_roofs\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "from_eave_m": {
- "description": "A spot on a module taken out before, or one corner of an area: horizontal metres up from the eave.",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "from_left_m": {
- "description": "Metres from the face's left end, seen from below.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "to_eave_m": {
- "description": "The area's other corner.",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "to_left_m": {
- "description": "The area's other corner.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "type": {
- "const": "restore_modules",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face",
- "from_eave_m",
- "from_left_m"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "face": {
- "description": "A face id from \"faces\" or \"dormer_roofs\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "from_eave_m": {
- "description": "A spot on (or next to) the field: the modules joined to the nearest one move together.",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "from_left_m": {
- "description": "Metres from the face's left end, seen from below.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "right_m": {
- "description": "Metres to the right seen from below (negative: left).",
- "maximum": 40,
- "minimum": -40,
- "type": "number"
- },
- "type": {
- "const": "move_field",
- "type": "string"
- },
- "up_m": {
- "description": "Metres up the roof, in its slope (negative: down).",
- "maximum": 20,
- "minimum": -20,
- "type": "number"
- }
- },
- "required": [
- "type",
- "face",
- "from_eave_m",
- "from_left_m"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "face": {
- "description": "A face id from \"faces\" or \"dormer_roofs\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "from_eave_m": {
- "description": "A spot on (or next to) the field.",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "from_left_m": {
- "description": "Metres from the face's left end, seen from below.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "type": {
- "const": "turn_field",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face",
- "from_eave_m",
- "from_left_m"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Move the modules in an area together, as the solar roof check moves a selection (press, hold and drag over them, then drag the middle). All or none: blocked by the face's edge, another module or an obstacle.",
- "properties": {
- "face": {
- "description": "A face id from \"faces\" or \"dormer_roofs\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "from_eave_m": {
- "description": "One corner of the area (horizontal metres up from the eave), or a spot on one module.",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "from_left_m": {
- "description": "Metres from the face's left end, seen from below.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "right_m": {
- "description": "Metres to the right seen from below (negative: left).",
- "maximum": 40,
- "minimum": -40,
- "type": "number"
- },
- "to_eave_m": {
- "description": "The area's other corner: every module whose middle lies in the area is picked.",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "to_left_m": {
- "description": "The area's other corner.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "type": {
- "const": "move_modules",
- "type": "string"
- },
- "up_m": {
- "description": "Metres up the roof, in its slope (negative: down).",
- "maximum": 20,
- "minimum": -20,
- "type": "number"
- }
- },
- "required": [
- "type",
- "face",
- "from_eave_m",
- "from_left_m"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Turn the modules in an area portrait ↔ landscape, as the page's selection does: laid afresh from their corner at the eave, as wide as they were, at most as many as there were. Modules on racks always stand landscape.",
- "properties": {
- "face": {
- "description": "A face id from \"faces\" or \"dormer_roofs\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "from_eave_m": {
- "description": "One corner of the area (horizontal metres up from the eave), or a spot on one module.",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "from_left_m": {
- "description": "Metres from the face's left end, seen from below.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "to_eave_m": {
- "description": "The area's other corner: every module whose middle lies in the area is picked.",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "to_left_m": {
- "description": "The area's other corner.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "type": {
- "const": "turn_modules",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face",
- "from_eave_m",
- "from_left_m"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Another module type for the modules in an area (the page's selection, \"Modultyp\"): each keeps its row and column from the group's corner; one that no longer fits goes.",
- "properties": {
- "face": {
- "description": "A face id from \"faces\" or \"dormer_roofs\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "from_eave_m": {
- "description": "One corner of the area (horizontal metres up from the eave), or a spot on one module.",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "from_left_m": {
- "description": "Metres from the face's left end, seen from below.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "module": {
- "description": "The module type for these modules: m470 = 470 W · 1.762 × 1.134 mm; m490 = 490 W · 1.800 × 1.134 mm; m520 = 520 W · 1.960 × 1.134 mm. The plan's own type (edit \"modules\") stays for the rest.",
- "enum": [
- "m470",
- "m490",
- "m520"
- ],
- "type": "string"
- },
- "to_eave_m": {
- "description": "The area's other corner: every module whose middle lies in the area is picked.",
- "maximum": 30,
- "minimum": 0,
- "type": "number"
- },
- "to_left_m": {
- "description": "The area's other corner.",
- "maximum": 40,
- "minimum": 0,
- "type": "number"
- },
- "type": {
- "const": "set_module_type",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face",
- "from_eave_m",
- "from_left_m",
- "module"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "id": {
- "description": "An obstacle id (\"u:<n>\" for one added before).",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "remove_obstacle",
- "type": "string"
- }
- },
- "required": [
- "type",
- "id"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "id": {
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "confirm_obstacle",
- "type": "string"
- }
- },
- "required": [
- "type",
- "id"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "on": {
- "description": "Modules on flat and shed dormers too (gable dormers always carry them).",
- "type": "boolean"
- },
- "type": {
- "const": "dormer_modules",
- "type": "string"
- }
- },
- "required": [
- "type",
- "on"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "„Gaube ganz belegen“: as many modules as fit on the dormer's roof, laid as any roof (on racks where its flat top is big enough or stands on a flat roof). A flat or shed dormer is switched on alone.",
- "properties": {
- "id": {
- "description": "A dormer's obstacle id (from \"obstacles\", \"u:<n>\" for one added).",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "fill_dormer",
- "type": "string"
- }
- },
- "required": [
- "type",
- "id"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "„Alle Module von der Gaube nehmen“: the dormer's roof switched off (fill_dormer lays it again).",
- "properties": {
- "id": {
- "description": "A dormer's obstacle id.",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "clear_dormer",
- "type": "string"
- }
- },
- "required": [
- "type",
- "id"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "arrange": {
- "description": "As the solar roof check names them: design = „Ästhetisch“ (whole rectangles only, the same height, left and right the same width), tidy = „Ordentlich“ (rows in one line, no small single fields; default), most = „Maximal“ (as many as fit, landscape in gaps too).",
- "enum": [
- "tidy",
- "most",
- "design"
- ],
- "type": "string"
- },
- "module": {
- "description": "The module type: m470 = 470 W · 1.762 × 1.134 mm; m490 = 490 W · 1.800 × 1.134 mm; m520 = 520 W · 1.960 × 1.134 mm. Hand-laid modules are laid again for the new size, rows and columns kept; \"applied\" says how many no longer fit.",
- "enum": [
- "m470",
- "m490",
- "m520"
- ],
- "type": "string"
- },
- "orientation": {
- "description": "auto (default): portrait, or landscape where that fits 2+ more modules; mixed: portrait and landscape on one roof, wherever each fits.",
- "enum": [
- "auto",
- "portrait",
- "landscape",
- "mixed"
- ],
- "type": "string"
- },
- "type": {
- "const": "modules",
- "type": "string"
- }
- },
- "required": [
- "type"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "id": {
- "description": "A building part from \"building_parts\" (an extension, a garage, the other half of a semi-detached house) to plan with the house.",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "add_part",
- "type": "string"
- }
- },
- "required": [
- "type",
- "id"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "id": {
- "description": "A building part from \"building_parts\" to plan without again.",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "remove_part",
- "type": "string"
- }
- },
- "required": [
- "type",
- "id"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "id": {
- "description": "A tree id (\"t:…\") from \"shade\": felled, or wrong in the data; its shade no longer counts.",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "remove_tree",
- "type": "string"
- }
- },
- "required": [
- "type",
- "id"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "id": {
- "description": "A tree id taken out before.",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "restore_tree",
- "type": "string"
- }
- },
- "required": [
- "type",
- "id"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "change_m": {
- "description": "Metres higher (positive) or lower (negative) than the laser scan measured it (the scan is from earlier years).",
- "maximum": 20,
- "minimum": -20,
- "type": "number"
- },
- "id": {
- "description": "A tree id (\"t:…\") from \"shade\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "tree_height",
- "type": "string"
- }
- },
- "required": [
- "type",
- "id",
- "change_m"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "corners": {
- "description": "A free four-cornered outline instead of a rectangle (as the solar roof check's corner handles): its corners on its face, the front (eave side) first.",
- "items": {
- "additionalProperties": false,
- "description": "A corner on the face: horizontal metres up from the eave and from the left end seen from below.",
- "properties": {
- "from_eave_m": {
- "maximum": 40,
- "minimum": -40,
- "type": "number"
- },
- "from_left_m": {
- "maximum": 50,
- "minimum": -50,
- "type": "number"
- }
- },
- "required": [
- "from_eave_m",
- "from_left_m"
- ],
- "type": "object"
- },
- "maxItems": 4,
- "minItems": 4,
- "type": "array"
- },
- "depth_m": {
- "description": "New depth up the roof, metres in the slope (a dormer's horizontally).",
- "maximum": 12,
- "minimum": 0.1,
- "type": "number"
- },
- "height_m": {
- "description": "How high it stands above the roof, metres (a chimney taller than measured casts more shade).",
- "maximum": 8,
- "minimum": 0.1,
- "type": "number"
- },
- "id": {
- "description": "An obstacle id from \"obstacles\" (found by scan or photo) or \"your_obstacles\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "right_m": {
- "description": "Move it to the right seen from below (negative: left), metres.",
- "maximum": 10,
- "minimum": -10,
- "type": "number"
- },
- "turn_deg": {
- "description": "Turn it round its centre, degrees (positive: clockwise seen from above), as „Drehen“ in the solar roof check.",
- "maximum": 180,
- "minimum": -180,
- "type": "number"
- },
- "type": {
- "const": "edit_obstacle",
- "type": "string"
- },
- "up_m": {
- "description": "Move it up the roof (negative: down), horizontal metres.",
- "maximum": 10,
- "minimum": -10,
- "type": "number"
- },
- "width_m": {
- "description": "New width along the eave, metres in the slope.",
- "maximum": 12,
- "minimum": 0.1,
- "type": "number"
- }
- },
- "required": [
- "type",
- "id"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "corners": {
- "description": "The face's new outline, corner by corner (as the solar roof check's editor drags them); \"faces[].corners\" gives the present ones.",
- "items": {
- "additionalProperties": false,
- "description": "A corner on the face: horizontal metres up from the eave and from the left end seen from below.",
- "properties": {
- "from_eave_m": {
- "maximum": 40,
- "minimum": -40,
- "type": "number"
- },
- "from_left_m": {
- "maximum": 50,
- "minimum": -50,
- "type": "number"
- }
- },
- "required": [
- "from_eave_m",
- "from_left_m"
- ],
- "type": "object"
- },
- "maxItems": 30,
- "minItems": 3,
- "type": "array"
- },
- "face": {
- "description": "A face id from the last answer's \"faces\".",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "face_outline",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face",
- "corners"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "„Wieder automatisch belegen“: faces laid by hand go back to the automatic layout, so orientation and arrangement count there again. Their hand-laid modules go; their outline, slope and turned racks stay (reset_face takes those back too).",
- "properties": {
- "face": {
- "description": "One face laid by hand; without it, all of them.",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "lay_automatically",
- "type": "string"
- }
- },
- "required": [
- "type"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "face": {
- "description": "A face back to the measured roof („Wie gemessen“): its outline, slope, turned racks and hand-laid modules go.",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "reset_face",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "face": {
- "description": "A piece of a split face (\"<id>|1\" or \"|2\"): both pieces become one face again.",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "unsplit",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "eave_height_m": {
- "description": "Its lowest edge above the ground, metres.",
- "maximum": 30,
- "minimum": 1.5,
- "type": "number"
- },
- "face": {
- "description": "A face added with add_face (\"d:<n>\").",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "drawn_face",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face",
- "eave_height_m"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "face": {
- "description": "A face added with add_face (\"d:<n>\"): deleted; later ones move down by one.",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "delete_face",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "id": {
- "description": "An obstacle id from \"obstacles\" or \"your_obstacles\" (a chimney, a skylight dome, a vent …): the aerial photo is searched for more of the same on this roof (as „Ähnliche finden“), and what it finds is added as your obstacles. One AI call each; at most 3 a call.",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "find_similar",
- "type": "string"
- }
- },
- "required": [
- "type",
- "id"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "The AI outline check (as „Umriss passt nicht?“): the roof's edges are compared with the aerial photo and moved where they clearly lie elsewhere. One AI call. plan_roof runs it by itself on a new plan when the photo speaks against the outline; call it when a note says the outline may be wrong, or the outline looks wrong to you, before placing modules: it replaces the outline of each face it moves, your own changes to that face included.",
- "properties": {
- "type": {
- "const": "check_outline",
- "type": "string"
- }
- },
- "required": [
- "type"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Ask the aerial photo again for chimneys and roof windows when \"obstacles_checked\" was false (as „Nochmal suchen“).",
- "properties": {
- "type": {
- "const": "search_again",
- "type": "string"
- }
- },
- "required": [
- "type"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "degrees": {
- "description": "Turn the module racks from the roof's own edges by this much (0: along the edges again; positive: anticlockwise, seen from above).",
- "maximum": 45,
- "minimum": -45,
- "type": "number"
- },
- "face": {
- "description": "A flat face, or a flat dormer roof from \"dormer_roofs\" whose \"mounting\" is racks.",
- "maxLength": 80,
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "const": "turn_racks",
- "type": "string"
- }
- },
- "required": [
- "type",
- "face",
- "degrees"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "properties": {
- "backup_power": {
- "description": "The person wants backup power (Notstrom): it is in both prices of the price guide.",
- "type": "boolean"
- },
- "battery_kwh": {
- "description": "Battery size, kWh: 0 for none, or a whole number from 4 to 20 (default 8).",
- "maximum": 20,
- "minimum": 0,
- "type": "integer"
- },
- "battery_type": {
- "description": "The battery's type, both DC-coupled hybrid systems: \"nv5\" (low voltage, the default) charges and discharges at most 5 kW whatever its size; \"hv1c\" (high voltage) at most 1C, as many kW as it has kWh (10 kWh: 10 kW, 4 kWh: only 4 kW). Only with a battery. It matters at peaks: an e-car charging at more than 5 kW; a household on its own comes out nearly the same with both, with a heat pump too.",
- "enum": [
- "nv5",
- "hv1c"
- ],
- "type": "string"
- },
- "consumption_kwh": {
- "description": "The household's electricity use a year, kWh, without car or heat pump (default 4,500).",
- "maximum": 30000,
- "minimum": 1000,
- "type": "number"
- },
- "ev": {
- "description": "An electric car is (or will be) charged at home: by default 15,000 km a year, about 2,955 kWh, charged in the evening (edit_roof device sets km, when and how fast).",
- "type": "boolean"
- },
- "heat_pump": {
- "description": "A heat pump heats the house: by default about 4,500 kWh a year, mostly in winter (edit_roof device sets the person's own figure).",
- "type": "boolean"
- },
- "offer_eur": {
- "description": "The total price on an offer the person already has, euros before any subsidy (0 clears it): the payback then counts it. Without one the payback counts the price guide's recommendation (summary.price_guide). Never pass a price of Sky Solar's as an offer.",
- "maximum": 300000,
- "minimum": 0,
- "type": "number"
- },
- "optimizers": {
- "description": "The person wants module optimizers: they are in both prices of the price guide. Without them the recommendation adds them anyway where the plan shows shade or several directions.",
- "type": "boolean"
- },
- "roof_step": {
- "description": "How demanding the roof is for the price guide: \"simple\" (one field, plain tiles), \"normal\", \"complex\" (several directions, many small fields, Biberschwanz tiles); \"auto\" (the default) works it out from the plan. Changes the price guide only.",
- "enum": [
- "auto",
- "simple",
- "normal",
- "complex"
- ],
- "type": "string"
- },
- "type": {
- "const": "household",
- "type": "string"
- }
- },
- "required": [
- "type"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "An e-car, a heat pump, its heating rod or a shiftable appliance with its own load curve: on or off, its settings and its colour (also the battery's colour). The car and the heat pump add to the household's day; appliances and the rod add no kWh, they give a share of the year a shape of its own. The answer's summary.devices shows each one's kWh a year and how much of it the roof covers; summary.device_colours the colours.",
- "properties": {
- "bivalence_c": {
- "description": "heating_rod: the outdoor temperature below which it helps, °C (default -5).",
- "maximum": 5,
- "minimum": -20,
- "type": "number"
- },
- "charge_kw": {
- "description": "ev: charging power, kW: 2.3, 3.7, 7.4, 11, 22 (default 11, a usual wallbox).",
- "type": "number"
- },
- "charges_per_week": {
- "description": "ev: charging sessions a week, 1 to 7 (default 7, every day); each takes what built up since the last. Not for \"weekend\".",
- "maximum": 7,
- "minimum": 1,
- "type": "integer"
- },
- "charging": {
- "description": "ev: when the car is at home to charge: \"evening\" (default, from 18:00), \"daytime\" (home office or retired, from 11:00), \"weekend\" (Saturday and Sunday from 11:00). It charges at full power until done; no smart charging yet.",
- "enum": [
- "evening",
- "daytime",
- "weekend"
- ],
- "type": "string"
- },
- "colour": {
- "description": "The device's colour as the 3D view draws it by the house (changes no figure): heat_pump and battery silver, white, anthracite, black (default silver); ev (the car's paint) silver, white, anthracite, black, blue, red (default silver). A colour alone does not count as an answer about km or kWh.",
- "enum": [
- "silver",
- "white",
- "anthracite",
- "black",
- "blue",
- "red"
- ],
- "type": "string"
- },
- "device": {
- "description": "\"ev\": an electric car charged at home (with its wallbox); \"heat_pump\": an air-to-water heat pump heating the house and its water; \"heating_rod\": the heat pump's electric rod (needs the heat pump; off by default); \"battery\": the home battery, only its colour (its size is household.battery_kwh, its type household.battery_type); \"dishwasher\", \"washer\", \"dryer\": a shiftable appliance run at a set time. An appliance adds nothing to the household's kWh a year: it is already inside them and moves out of the smooth curve into a timed shape. Fridge, freezer and TV stay in the base load.",
- "enum": [
- "ev",
- "heat_pump",
- "heating_rod",
- "battery",
- "dishwasher",
- "washer",
- "dryer"
- ],
- "type": "string"
- },
- "hot_water_percent": {
- "description": "heating_rod: share of the hot water it heats instead of the heat pump, % (default 0), each morning. The rod's kWh stay inside the heat pump's kWh a year.",
- "maximum": 100,
- "minimum": 0,
- "type": "number"
- },
- "km_per_year": {
- "description": "ev: km a year charged at home (default 15000); at 19.7 kWh per 100 km (ADAC Ecotest 2023 average, at the socket).",
- "maximum": 100000,
- "minimum": 1000,
- "type": "number"
- },
- "kwh_per_run": {
- "description": "appliance: kWh per run (defaults: dishwasher 0.85, washer 0.8, dryer 1.5; EU label and Verbraucherzentrale figures; a dryer without a heat pump about 3.4).",
- "maximum": 6,
- "minimum": 0.1,
- "type": "number"
- },
- "kwh_per_year": {
- "anyOf": [
- {
- "maximum": 30000,
- "minimum": 500,
- "type": "number"
- },
- {
- "const": "estimate",
- "type": "string"
- }
- ],
- "description": "heat_pump: its electricity a year in kWh (its meter, the bill or the installer's figure), or \"estimate\" for ours (4,500 kWh). Spread over the year by the measured outdoor temperature at the house."
- },
- "on": {
- "description": "false takes the device out (its settings go with it); true or left out puts it in. Sending a device edit, even with no settings, records that the person answered for it (the defaults are right), so ask_the_person stops asking.",
- "type": "boolean"
- },
- "peak_kw": {
- "description": "appliance: kW of its heating peak (defaults: dishwasher 2, washer 2, dryer 0.8).",
- "maximum": 4,
- "minimum": 0.1,
- "type": "number"
- },
- "peak_minutes": {
- "description": "appliance: how long the peak lasts, minutes, all of it together (defaults: dishwasher 20, washer 15, dryer 100).",
- "maximum": 240,
- "minimum": 5,
- "type": "number"
- },
- "rod_kw": {
- "description": "heating_rod: its power, kW (default 6; rods have 3 to 9 kW).",
- "maximum": 12,
- "minimum": 1,
- "type": "number"
- },
- "runs_per_week": {
- "description": "appliance: runs a week, evenly over the week (defaults: dishwasher 5.4, washer 4.2, dryer 2).",
- "maximum": 14,
- "minimum": 0.5,
- "type": "number"
- },
- "start": {
- "description": "appliance: when it starts, \"HH:MM\" (to ten minutes) or \"sun\" for the midday window, a fixed time as a delay timer sets it (dishwasher 12:00, washer 11:00, dryer 14:00). Defaults are evening: dishwasher 20:00, washer 18:00, dryer 20:00.",
- "pattern": "^(sun|([01]?\\d|2[0-3]):[0-5]\\d)$",
- "type": "string"
- },
- "type": {
- "const": "device",
- "type": "string"
- },
- "wallbox_colour": {
- "description": "ev: the wallbox's colour, white, silver, anthracite, black (default white).",
- "enum": [
- "white",
- "silver",
- "anthracite",
- "black"
- ],
- "type": "string"
- }
- },
- "required": [
- "type",
- "device"
- ],
- "type": "object"
- }
-]New value: +[
+ {
+ "additionalProperties": false,
+ "properties": {
+ "direction_deg": {
+ "description": "Compass direction the face looks to: 0 north, 90 east, 180 south, 270 west.",
+ "maximum": 360,
+ "minimum": 0,
+ "type": "number"
+ },
+ "face": {
+ "description": "A face id from the last answer's \"faces\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "tilt_deg": {
+ "description": "Tilt, degrees (0 flat).",
+ "maximum": 60,
+ "minimum": 0,
+ "type": "number"
+ },
+ "type": {
+ "const": "slope",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "edge": {
+ "description": "Which edge moves: the eave, or the left or right end seen from below.",
+ "enum": [
+ "eave",
+ "left",
+ "right"
+ ],
+ "type": "string"
+ },
+ "face": {
+ "description": "A face id from the last answer's \"faces\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "metres": {
+ "description": "Or a change: metres out from where the edge is now (negative: in), horizontal.",
+ "maximum": 1.5,
+ "minimum": -1.5,
+ "type": "number"
+ },
+ "to_m": {
+ "description": "Eave only: how far the eave should reach past the wall, horizontal metres (\"Dachüberstand 50 cm\" → 0.5). The face list shows the current eave_overhang_m.",
+ "maximum": 1.5,
+ "minimum": 0,
+ "type": "number"
+ },
+ "type": {
+ "const": "overhang",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face",
+ "edge"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "face": {
+ "description": "A face id from the last answer's \"faces\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "from_eave_m": {
+ "description": "A cut along the eave, this far up from it.",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "from_left_m": {
+ "description": "A cut up the slope, this far from the left end.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "type": {
+ "const": "split",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "face": {
+ "description": "A face id from the last answer's \"faces\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "remove_face",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "face": {
+ "description": "A face id from the last answer's \"faces\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "restore_face",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "corners_m": {
+ "description": "Any shape instead: its corners as [east, north] metres in the same frame as \"house.footprint_m\" (from a building plan).",
+ "items": {
+ "items": false,
+ "maxItems": 2,
+ "minItems": 2,
+ "prefixItems": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "number"
+ }
+ ],
+ "type": "array"
+ },
+ "maxItems": 24,
+ "minItems": 3,
+ "type": "array"
+ },
+ "depth_m": {
+ "description": "Out from the wall, metres.",
+ "maximum": 20,
+ "minimum": 1,
+ "type": "number"
+ },
+ "direction_deg": {
+ "description": "Way the roof slopes down to (default: away from the wall).",
+ "maximum": 360,
+ "minimum": 0,
+ "type": "number"
+ },
+ "eave_height_m": {
+ "description": "Height of the roof's lowest edge above the ground, metres.",
+ "maximum": 15,
+ "minimum": 1.5,
+ "type": "number"
+ },
+ "offset_m": {
+ "description": "From the middle of the wall: positive to the east (north and south walls) or north (east and west walls).",
+ "maximum": 30,
+ "minimum": -30,
+ "type": "number"
+ },
+ "tilt_deg": {
+ "description": "Roof tilt, degrees (default 0, flat).",
+ "maximum": 60,
+ "minimum": 0,
+ "type": "number"
+ },
+ "type": {
+ "const": "add_face",
+ "type": "string"
+ },
+ "wall": {
+ "description": "The house wall the new part stands against (an extension, a garage, a porch). With width_m and depth_m; or give corners_m instead.",
+ "enum": [
+ "north",
+ "east",
+ "south",
+ "west"
+ ],
+ "type": "string"
+ },
+ "width_m": {
+ "description": "Along the wall, metres.",
+ "maximum": 30,
+ "minimum": 1,
+ "type": "number"
+ }
+ },
+ "required": [
+ "type",
+ "eave_height_m"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "depth_m": {
+ "description": "Its depth up the roof, metres in the slope (a roof window \"78 × 140\": 1.4); a dormer's horizontally, as on a drawing from above. A ladder starts at from_eave_m and runs this far up; without it a ladder runs from the eave to the ridge.",
+ "maximum": 12,
+ "minimum": 0.1,
+ "type": "number"
+ },
+ "face": {
+ "description": "A face id from the last answer's \"faces\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "from_eave_m": {
+ "description": "Horizontal metres up from the eave (as on a drawing seen from above).",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "from_left_m": {
+ "description": "Metres from the face's left end, seen from below.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "height_m": {
+ "description": "How high it stands above the roof, metres (a dormer: its front wall; default: the kind's usual height).",
+ "maximum": 4,
+ "minimum": 0.1,
+ "type": "number"
+ },
+ "kind": {
+ "description": "What it is: chimney, window, vent, flue, dish, antenna, ladder, snowWood, snowGrid, steps, solarThermal, pv, dome, dormerShed, dormerFlat, dormerGable, dormerTwin, dormerHip, dormerPoint, dormerTrapez, dormerBarrel, dormerBat, dormerWall, or block (anything else, 60 cm square).",
+ "enum": [
+ "chimney",
+ "window",
+ "vent",
+ "flue",
+ "dish",
+ "antenna",
+ "ladder",
+ "snowWood",
+ "snowGrid",
+ "steps",
+ "solarThermal",
+ "pv",
+ "dome",
+ "dormerShed",
+ "dormerFlat",
+ "dormerGable",
+ "dormerTwin",
+ "dormerHip",
+ "dormerPoint",
+ "dormerTrapez",
+ "dormerBarrel",
+ "dormerBat",
+ "dormerWall",
+ "block"
+ ],
+ "type": "string"
+ },
+ "type": {
+ "const": "add_obstacle",
+ "type": "string"
+ },
+ "width_m": {
+ "description": "Its width along the eave, metres in the roof's slope (a dormer, a roof window \"78 × 140\": 0.78). Default: the kind's usual size.",
+ "maximum": 12,
+ "minimum": 0.1,
+ "type": "number"
+ }
+ },
+ "required": [
+ "type",
+ "face",
+ "kind",
+ "from_eave_m",
+ "from_left_m"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "depth_m": {
+ "description": "A new depth up the roof, metres in the slope (a dormer's horizontally).",
+ "maximum": 12,
+ "minimum": 0.1,
+ "type": "number"
+ },
+ "id": {
+ "description": "An obstacle id from \"obstacles\" or \"your_obstacles\" (e.g. a dormer the scan found). A found one becomes one of your obstacles (u:<n>), but its id keeps working: later edits that name it (edit_obstacle, change_kind, fill_dormer, clear_dormer, find_similar, remove_obstacle, confirm_obstacle) and its dormer roofs (g:<id>:<n>) reach the new one, in this call and later ones, while it stays on the found one's spot; the answer's \"id_map\" lists them ({ found id: u:<n> }). A dormer's modules and roof settings move to its new roofs where they are the same, and the answer says what did not.",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "kind": {
+ "description": "The new kind. Dormers: dormerShed, dormerFlat, dormerGable, dormerTwin, dormerHip, dormerPoint, dormerTrapez, dormerBarrel, dormerBat, dormerWall (a dormer stays its size). Or chimney, window, vent, flue, dish, antenna, ladder, snowWood, snowGrid, steps, solarThermal, pv, dome, block.",
+ "enum": [
+ "chimney",
+ "window",
+ "vent",
+ "flue",
+ "dish",
+ "antenna",
+ "ladder",
+ "snowWood",
+ "snowGrid",
+ "steps",
+ "solarThermal",
+ "pv",
+ "dome",
+ "dormerShed",
+ "dormerFlat",
+ "dormerGable",
+ "dormerTwin",
+ "dormerHip",
+ "dormerPoint",
+ "dormerTrapez",
+ "dormerBarrel",
+ "dormerBat",
+ "dormerWall",
+ "block"
+ ],
+ "type": "string"
+ },
+ "type": {
+ "const": "change_kind",
+ "type": "string"
+ },
+ "width_m": {
+ "description": "A new width along the eave, metres in the slope.",
+ "maximum": 12,
+ "minimum": 0.1,
+ "type": "number"
+ }
+ },
+ "required": [
+ "type",
+ "id",
+ "kind"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "colour": {
+ "description": "A colour id. metal: anthracite, aluminium, zinc, copper, brown; module: black, blue; dormerBarrel: tiles, anthracite, aluminium, zinc, copper, brown; dormerShed: tiles, anthracite, aluminium, zinc, copper, brown; block: wall, grey, anthracite, red, brown; window: silver, anthracite, white, brown; chimney: render, brick, slate, steel, copper; flue: steel, anthracite, red; vent: anthracite, red, brown, grey; dish: light, anthracite, red; antenna: silver, anthracite; ladder: anthracite, red, brown, galvanised; snowWood: natural, dark, weathered; snowGrid: anthracite, red, brown, galvanised; steps: anthracite, red, brown, galvanised; solarThermal: silver, black; pv: silver, black; dome: opal, clear",
+ "maxLength": 30,
+ "minLength": 1,
+ "type": "string"
+ },
+ "face": {
+ "description": "With item \"metal\": only this face's sheet (a copper garage beside a zinc roof).",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "item": {
+ "description": "What gets the colour: roof (a tiled roof, for the building part of face, the house without face; colours: photo, clay, red, darkRed, copper, brown, black, anthracite, slate, grey), metal (sheet-metal dormer tops and Falz- or Trapezblech roofs), or a kind of item.",
+ "enum": [
+ "roof",
+ "metal",
+ "module",
+ "dormerBarrel",
+ "dormerShed",
+ "block",
+ "window",
+ "chimney",
+ "flue",
+ "vent",
+ "dish",
+ "antenna",
+ "ladder",
+ "snowWood",
+ "snowGrid",
+ "steps",
+ "solarThermal",
+ "pv",
+ "dome"
+ ],
+ "type": "string"
+ },
+ "type": {
+ "const": "colour",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "item",
+ "colour"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "covering": {
+ "description": "The roof's covering: pfanne (Doppel-S-Pfanne, the common concrete tile, the standard of a new plan), tiles (Geradschnitt, a straight-cut tile, the standard of older plans), ton (Tonziegel, a narrow interlocking clay tile; mounted like tiles), biber (Biberschwanz plain tiles), flach (flat tiles such as Tegalit), reform (Reformziegel, one broad flat trough), doppelmulde (Doppelmuldenfalzziegel, two narrow troughs), hohlfalz (Hohlfalzziegel, an S-wave; these four mounted like tiles), falz (standing-seam sheet, modules flush) or trapez (trapezoidal sheet).",
+ "enum": [
+ "tiles",
+ "ton",
+ "biber",
+ "flach",
+ "falz",
+ "trapez",
+ "pfanne",
+ "reform",
+ "doppelmulde",
+ "hohlfalz"
+ ],
+ "type": "string"
+ },
+ "face": {
+ "description": "Only this face (a garage in Falzblech, say), from \"faces\" or \"dormer_roofs\"; without it the whole roof. On a flat dormer roof it decides the mounting: tiles (a plain flat roof) or trapez puts the modules on racks, falz lays them flush (without a choice: racks where it stands on a flat roof or measures 20 m² or more; see its \"mounting\").",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "flat_rack": {
+ "description": "Racks on flat roofs: ew (east-west pairs back to back, default), south (rows facing south), ns (along the roof).",
+ "enum": [
+ "ew",
+ "south",
+ "ns"
+ ],
+ "type": "string"
+ },
+ "rack_tilt_deg": {
+ "anyOf": [
+ {
+ "const": 5,
+ "type": "number"
+ },
+ {
+ "const": 10,
+ "type": "number"
+ },
+ {
+ "const": 15,
+ "type": "number"
+ }
+ ],
+ "description": "Tilt of the racks on flat roofs and racked trapezoidal sheet: 5, 10 (default) or 15 degrees."
+ },
+ "trapez_racks": {
+ "description": "Trapezoidal sheet sloping 15° or less: modules on racks instead of flush.",
+ "type": "boolean"
+ },
+ "type": {
+ "const": "covering",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Place an R × C field of modules exactly where the person's drawing shows it, in the face's own metres. Use dry_run first when unsure.",
+ "properties": {
+ "anchor": {
+ "description": "What from_eave_m and from_left_m name: the field's lower left corner (default) or its middle.",
+ "enum": [
+ "lower_left",
+ "centre"
+ ],
+ "type": "string"
+ },
+ "columns": {
+ "description": "Modules side by side (default 1).",
+ "maximum": 20,
+ "minimum": 1,
+ "type": "number"
+ },
+ "dry_run": {
+ "description": "true: change nothing; the answer's \"dry_run\" lists the modules that would be placed (row, column, centre, orientation) and every collision with its reason.",
+ "type": "boolean"
+ },
+ "exact": {
+ "description": "Default true: all or nothing. If any module of the R × C field would collide (an obstacle, the edge margin, another module) nothing is placed and every collision is listed. false: place the ones that fit (as add_modules).",
+ "type": "boolean"
+ },
+ "face": {
+ "description": "A face id from \"faces\" or \"dormer_roofs\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "from_eave_m": {
+ "description": "The field's anchor: horizontal metres up from the eave (the lower left corner, or the middle with anchor \"centre\").",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "from_left_m": {
+ "description": "The anchor: metres from the face's left end, seen from below.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "orientation": {
+ "description": "portrait: the module's long side up the slope (across the eave); landscape: long side along the eave. The same on every face, house roof or dormer roof: \"up the slope\" and \"along the eave\" are the face's own axes (geometry.faces[].slope_axis / eave_axis; on a flat face its longest edge). On racks every module stands landscape and this does not apply.",
+ "enum": [
+ "portrait",
+ "landscape"
+ ],
+ "type": "string"
+ },
+ "rows": {
+ "description": "Rows up the roof (default 1).",
+ "maximum": 10,
+ "minimum": 1,
+ "type": "number"
+ },
+ "snap_to_grid": {
+ "description": "true: the field's first module is moved onto the module grid of the nearest module on this face (same pitch, same rows and columns, its orientation unless given), so a new field lines up with an existing one. The answer says by how many cm it moved.",
+ "type": "boolean"
+ },
+ "type": {
+ "const": "place_field",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face",
+ "from_eave_m",
+ "from_left_m"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Continue a row (or column) of modules in a direction, on the row's own grid: the same pitch, orientation, type and line, one clamp gap on from its last module. A neighbouring field on the same grid is met exactly.",
+ "properties": {
+ "count": {
+ "description": "How many modules to add. Without it: as many as fit, up to the next obstacle, module or edge (the answer says what stopped the row).",
+ "maximum": 20,
+ "minimum": 1,
+ "type": "integer"
+ },
+ "direction": {
+ "description": "Which way: right or left seen from below along the eave; up or down the slope.",
+ "enum": [
+ "right",
+ "left",
+ "up",
+ "down"
+ ],
+ "type": "string"
+ },
+ "dry_run": {
+ "description": "true: change nothing; \"dry_run\" lists what would be added.",
+ "type": "boolean"
+ },
+ "face": {
+ "description": "A face id from \"faces\" or \"dormer_roofs\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "from_eave_m": {
+ "description": "A spot on a module of the row (or column) to continue.",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "from_left_m": {
+ "description": "Metres from the face's left end, seen from below.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "type": {
+ "const": "continue_row",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face",
+ "from_eave_m",
+ "from_left_m",
+ "direction"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Move a dormer and keep its modules: the dormer moves as with edit_obstacle, its automatically laid modules follow, and the modules laid by hand on its roofs are carried the same distance.",
+ "properties": {
+ "id": {
+ "description": "A dormer's obstacle id (\"obstacles\" or \"your_obstacles\"; a replaced dormer's old id works).",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "right_m": {
+ "description": "Move it to the right seen from below (negative: left), metres.",
+ "maximum": 10,
+ "minimum": -10,
+ "type": "number"
+ },
+ "type": {
+ "const": "move_dormer",
+ "type": "string"
+ },
+ "up_m": {
+ "description": "Move it up the roof (negative: down), horizontal metres.",
+ "maximum": 10,
+ "minimum": -10,
+ "type": "number"
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "face": {
+ "description": "A face id from \"faces\" or \"dormer_roofs\". Its modules stay exactly where they are from now on (the other module edits do this by themselves).",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "lay_by_hand",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "columns": {
+ "description": "Modules side by side (default 1).",
+ "maximum": 20,
+ "minimum": 1,
+ "type": "number"
+ },
+ "face": {
+ "description": "A face id from \"faces\" or \"dormer_roofs\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "from_eave_m": {
+ "description": "The field's lower left corner: horizontal metres up from the eave.",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "from_left_m": {
+ "description": "The field's lower left corner: metres from the face's left end, seen from below.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "orientation": {
+ "description": "Default portrait (landscape if the plan is set to landscape). portrait: the module's long side up the slope (across the eave); landscape: long side along the eave. The same on every face, house roof or dormer roof: \"up the slope\" and \"along the eave\" are the face's own axes (geometry.faces[].slope_axis / eave_axis; on a flat face its longest edge). On racks every module stands landscape and this does not apply.",
+ "enum": [
+ "portrait",
+ "landscape"
+ ],
+ "type": "string"
+ },
+ "rows": {
+ "description": "Rows up the roof (default 1).",
+ "maximum": 10,
+ "minimum": 1,
+ "type": "number"
+ },
+ "type": {
+ "const": "add_modules",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face",
+ "from_eave_m",
+ "from_left_m"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "face": {
+ "description": "A face id from \"faces\" or \"dormer_roofs\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "from_eave_m": {
+ "description": "A spot on the module to take off, or one corner of an area: horizontal metres up from the eave.",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "from_left_m": {
+ "description": "Metres from the face's left end, seen from below.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "to_eave_m": {
+ "description": "The area's other corner (every module whose centre lies inside goes).",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "to_left_m": {
+ "description": "The area's other corner.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "type": {
+ "const": "remove_modules",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face",
+ "from_eave_m",
+ "from_left_m"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Put modules taken out of the automatic layout back.",
+ "properties": {
+ "face": {
+ "description": "A face id from \"faces\" or \"dormer_roofs\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "from_eave_m": {
+ "description": "A spot on a module taken out before, or one corner of an area: horizontal metres up from the eave.",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "from_left_m": {
+ "description": "Metres from the face's left end, seen from below.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "to_eave_m": {
+ "description": "The area's other corner.",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "to_left_m": {
+ "description": "The area's other corner.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "type": {
+ "const": "restore_modules",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face",
+ "from_eave_m",
+ "from_left_m"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "face": {
+ "description": "A face id from \"faces\" or \"dormer_roofs\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "from_eave_m": {
+ "description": "A spot on (or next to) the field: the modules joined to the nearest one move together.",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "from_left_m": {
+ "description": "Metres from the face's left end, seen from below.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "right_m": {
+ "description": "Metres to the right seen from below (negative: left).",
+ "maximum": 40,
+ "minimum": -40,
+ "type": "number"
+ },
+ "type": {
+ "const": "move_field",
+ "type": "string"
+ },
+ "up_m": {
+ "description": "Metres up the roof, in its slope (negative: down).",
+ "maximum": 20,
+ "minimum": -20,
+ "type": "number"
+ }
+ },
+ "required": [
+ "type",
+ "face",
+ "from_eave_m",
+ "from_left_m"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "face": {
+ "description": "A face id from \"faces\" or \"dormer_roofs\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "from_eave_m": {
+ "description": "A spot on (or next to) the field.",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "from_left_m": {
+ "description": "Metres from the face's left end, seen from below.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "type": {
+ "const": "turn_field",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face",
+ "from_eave_m",
+ "from_left_m"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Move the modules in an area together, as the solar roof check moves a selection (press, hold and drag over them, then drag the middle). All or none: blocked by the face's edge, another module or an obstacle.",
+ "properties": {
+ "face": {
+ "description": "A face id from \"faces\" or \"dormer_roofs\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "from_eave_m": {
+ "description": "One corner of the area (horizontal metres up from the eave), or a spot on one module.",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "from_left_m": {
+ "description": "Metres from the face's left end, seen from below.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "right_m": {
+ "description": "Metres to the right seen from below (negative: left).",
+ "maximum": 40,
+ "minimum": -40,
+ "type": "number"
+ },
+ "to_eave_m": {
+ "description": "The area's other corner: every module whose middle lies in the area is picked.",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "to_left_m": {
+ "description": "The area's other corner.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "type": {
+ "const": "move_modules",
+ "type": "string"
+ },
+ "up_m": {
+ "description": "Metres up the roof, in its slope (negative: down).",
+ "maximum": 20,
+ "minimum": -20,
+ "type": "number"
+ }
+ },
+ "required": [
+ "type",
+ "face",
+ "from_eave_m",
+ "from_left_m"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Turn the modules in an area portrait ↔ landscape, as the page's selection does: laid afresh from their corner at the eave, as wide as they were, at most as many as there were. Modules on racks always stand landscape.",
+ "properties": {
+ "face": {
+ "description": "A face id from \"faces\" or \"dormer_roofs\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "from_eave_m": {
+ "description": "One corner of the area (horizontal metres up from the eave), or a spot on one module.",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "from_left_m": {
+ "description": "Metres from the face's left end, seen from below.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "to_eave_m": {
+ "description": "The area's other corner: every module whose middle lies in the area is picked.",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "to_left_m": {
+ "description": "The area's other corner.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "type": {
+ "const": "turn_modules",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face",
+ "from_eave_m",
+ "from_left_m"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Another module type for the modules in an area (the page's selection, \"Modultyp\"): each keeps its row and column from the group's corner; one that no longer fits goes.",
+ "properties": {
+ "face": {
+ "description": "A face id from \"faces\" or \"dormer_roofs\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "from_eave_m": {
+ "description": "One corner of the area (horizontal metres up from the eave), or a spot on one module.",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "from_left_m": {
+ "description": "Metres from the face's left end, seen from below.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "module": {
+ "description": "The module type for these modules: m470 = 470 W · 1.762 × 1.134 mm; m490 = 490 W · 1.800 × 1.134 mm; m520 = 520 W · 1.960 × 1.134 mm; m410s = 410 W silver · 1.762 × 1.134 mm; m410g = 410 W green · 1.762 × 1.134 mm; m400w = 400 W wine red · 1.762 × 1.134 mm; m400t = 400 W terracotta · 1.762 × 1.134 mm. The plan's own type (edit \"modules\") stays for the rest.",
+ "enum": [
+ "m470",
+ "m490",
+ "m520",
+ "m410s",
+ "m410g",
+ "m400w",
+ "m400t"
+ ],
+ "type": "string"
+ },
+ "to_eave_m": {
+ "description": "The area's other corner: every module whose middle lies in the area is picked.",
+ "maximum": 30,
+ "minimum": 0,
+ "type": "number"
+ },
+ "to_left_m": {
+ "description": "The area's other corner.",
+ "maximum": 40,
+ "minimum": 0,
+ "type": "number"
+ },
+ "type": {
+ "const": "set_module_type",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face",
+ "from_eave_m",
+ "from_left_m",
+ "module"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "description": "An obstacle id (\"u:<n>\" for one added before).",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "remove_obstacle",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "confirm_obstacle",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "on": {
+ "description": "Modules on flat and shed dormers too (gable dormers always carry them).",
+ "type": "boolean"
+ },
+ "type": {
+ "const": "dormer_modules",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "on"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "„Gaube ganz belegen“: as many modules as fit on the dormer's roof, laid as any roof (on racks where its flat top is big enough or stands on a flat roof). A flat or shed dormer is switched on alone.",
+ "properties": {
+ "id": {
+ "description": "A dormer's obstacle id (from \"obstacles\", \"u:<n>\" for one added).",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "fill_dormer",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "„Alle Module von der Gaube nehmen“: the dormer's roof switched off (fill_dormer lays it again).",
+ "properties": {
+ "id": {
+ "description": "A dormer's obstacle id.",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "clear_dormer",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "arrange": {
+ "description": "As the solar roof check names them: design = „Ästhetisch“ (whole rectangles only, the same height, left and right the same width), tidy = „Ordentlich“ (rows in one line, no small single fields; default), most = „Maximal“ (as many as fit, landscape in gaps too).",
+ "enum": [
+ "tidy",
+ "most",
+ "design"
+ ],
+ "type": "string"
+ },
+ "module": {
+ "description": "The module type: m470 = 470 W · 1.762 × 1.134 mm; m490 = 490 W · 1.800 × 1.134 mm; m520 = 520 W · 1.960 × 1.134 mm; m410s = 410 W silver · 1.762 × 1.134 mm; m410g = 410 W green · 1.762 × 1.134 mm; m400w = 400 W wine red · 1.762 × 1.134 mm; m400t = 400 W terracotta · 1.762 × 1.134 mm. Hand-laid modules are laid again for the new size, rows and columns kept; \"applied\" says how many no longer fit.",
+ "enum": [
+ "m470",
+ "m490",
+ "m520",
+ "m410s",
+ "m410g",
+ "m400w",
+ "m400t"
+ ],
+ "type": "string"
+ },
+ "orientation": {
+ "description": "auto (default): portrait, or landscape where that fits 2+ more modules; mixed: portrait and landscape on one roof, wherever each fits.",
+ "enum": [
+ "auto",
+ "portrait",
+ "landscape",
+ "mixed"
+ ],
+ "type": "string"
+ },
+ "type": {
+ "const": "modules",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "description": "A building part from \"building_parts\" (an extension, a garage, the other half of a semi-detached house) to plan with the house.",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "add_part",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "description": "A building part from \"building_parts\" to plan without again.",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "remove_part",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "description": "A tree id (\"t:…\") from \"shade\": felled, or wrong in the data; its shade no longer counts.",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "remove_tree",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "description": "A tree id taken out before.",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "restore_tree",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "change_m": {
+ "description": "Metres higher (positive) or lower (negative) than the laser scan measured it (the scan is from earlier years).",
+ "maximum": 20,
+ "minimum": -20,
+ "type": "number"
+ },
+ "id": {
+ "description": "A tree id (\"t:…\") from \"shade\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "tree_height",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "id",
+ "change_m"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "corners": {
+ "description": "A free four-cornered outline instead of a rectangle (as the solar roof check's corner handles): its corners on its face, the front (eave side) first.",
+ "items": {
+ "additionalProperties": false,
+ "description": "A corner on the face: horizontal metres up from the eave and from the left end seen from below.",
+ "properties": {
+ "from_eave_m": {
+ "maximum": 40,
+ "minimum": -40,
+ "type": "number"
+ },
+ "from_left_m": {
+ "maximum": 50,
+ "minimum": -50,
+ "type": "number"
+ }
+ },
+ "required": [
+ "from_eave_m",
+ "from_left_m"
+ ],
+ "type": "object"
+ },
+ "maxItems": 4,
+ "minItems": 4,
+ "type": "array"
+ },
+ "depth_m": {
+ "description": "New depth up the roof, metres in the slope (a dormer's horizontally).",
+ "maximum": 12,
+ "minimum": 0.1,
+ "type": "number"
+ },
+ "height_m": {
+ "description": "How high it stands above the roof, metres (a chimney taller than measured casts more shade).",
+ "maximum": 8,
+ "minimum": 0.1,
+ "type": "number"
+ },
+ "id": {
+ "description": "An obstacle id from \"obstacles\" (found by scan or photo) or \"your_obstacles\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "right_m": {
+ "description": "Move it to the right seen from below (negative: left), metres.",
+ "maximum": 10,
+ "minimum": -10,
+ "type": "number"
+ },
+ "turn_deg": {
+ "description": "Turn it round its centre, degrees (positive: clockwise seen from above), as „Drehen“ in the solar roof check.",
+ "maximum": 180,
+ "minimum": -180,
+ "type": "number"
+ },
+ "type": {
+ "const": "edit_obstacle",
+ "type": "string"
+ },
+ "up_m": {
+ "description": "Move it up the roof (negative: down), horizontal metres.",
+ "maximum": 10,
+ "minimum": -10,
+ "type": "number"
+ },
+ "width_m": {
+ "description": "New width along the eave, metres in the slope.",
+ "maximum": 12,
+ "minimum": 0.1,
+ "type": "number"
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "corners": {
+ "description": "The face's new outline, corner by corner (as the solar roof check's editor drags them); \"faces[].corners\" gives the present ones.",
+ "items": {
+ "additionalProperties": false,
+ "description": "A corner on the face: horizontal metres up from the eave and from the left end seen from below.",
+ "properties": {
+ "from_eave_m": {
+ "maximum": 40,
+ "minimum": -40,
+ "type": "number"
+ },
+ "from_left_m": {
+ "maximum": 50,
+ "minimum": -50,
+ "type": "number"
+ }
+ },
+ "required": [
+ "from_eave_m",
+ "from_left_m"
+ ],
+ "type": "object"
+ },
+ "maxItems": 30,
+ "minItems": 3,
+ "type": "array"
+ },
+ "face": {
+ "description": "A face id from the last answer's \"faces\".",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "face_outline",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face",
+ "corners"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "„Wieder automatisch belegen“: faces laid by hand go back to the automatic layout, so orientation and arrangement count there again. Their hand-laid modules go; their outline, slope and turned racks stay (reset_face takes those back too).",
+ "properties": {
+ "face": {
+ "description": "One face laid by hand; without it, all of them.",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "lay_automatically",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "face": {
+ "description": "A face back to the measured roof („Wie gemessen“): its outline, slope, turned racks and hand-laid modules go.",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "reset_face",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "face": {
+ "description": "A piece of a split face (\"<id>|1\" or \"|2\"): both pieces become one face again.",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "unsplit",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "eave_height_m": {
+ "description": "Its lowest edge above the ground, metres.",
+ "maximum": 30,
+ "minimum": 1.5,
+ "type": "number"
+ },
+ "face": {
+ "description": "A face added with add_face (\"d:<n>\").",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "drawn_face",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face",
+ "eave_height_m"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "face": {
+ "description": "A face added with add_face (\"d:<n>\"): deleted; later ones move down by one.",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "delete_face",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "description": "An obstacle id from \"obstacles\" or \"your_obstacles\" (a chimney, a skylight dome, a vent …): the aerial photo is searched for more of the same on this roof (as „Ähnliche finden“), and what it finds is added as your obstacles. One AI call each; at most 3 a call.",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "find_similar",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "The AI outline check (as „Umriss passt nicht?“): the roof's edges are compared with the aerial photo and moved where they clearly lie elsewhere. One AI call. plan_roof runs it by itself on a new plan when the photo speaks against the outline; call it when a note says the outline may be wrong, or the outline looks wrong to you, before placing modules: it replaces the outline of each face it moves, your own changes to that face included.",
+ "properties": {
+ "type": {
+ "const": "check_outline",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Ask the aerial photo again for chimneys and roof windows when \"obstacles_checked\" was false (as „Nochmal suchen“).",
+ "properties": {
+ "type": {
+ "const": "search_again",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "degrees": {
+ "description": "Turn the module racks from the roof's own edges by this much (0: along the edges again; positive: anticlockwise, seen from above).",
+ "maximum": 45,
+ "minimum": -45,
+ "type": "number"
+ },
+ "face": {
+ "description": "A flat face, or a flat dormer roof from \"dormer_roofs\" whose \"mounting\" is racks.",
+ "maxLength": 80,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": {
+ "const": "turn_racks",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "face",
+ "degrees"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "backup_power": {
+ "description": "The person wants backup power (Notstrom): it is in both prices of the price guide.",
+ "type": "boolean"
+ },
+ "battery_kwh": {
+ "description": "Battery size, kWh: 0 for none, or a whole number from 4 to 20 (default 8).",
+ "maximum": 20,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "battery_type": {
+ "description": "The battery's type, both DC-coupled hybrid systems: \"nv5\" (low voltage, the default) charges and discharges at most 5 kW whatever its size; \"hv1c\" (high voltage) at most 1C, as many kW as it has kWh (10 kWh: 10 kW, 4 kWh: only 4 kW). Only with a battery. It matters at peaks: an e-car charging at more than 5 kW; a household on its own comes out nearly the same with both, with a heat pump too.",
+ "enum": [
+ "nv5",
+ "hv1c"
+ ],
+ "type": "string"
+ },
+ "consumption_kwh": {
+ "description": "The household's electricity use a year, kWh, without car or heat pump (default 4,500).",
+ "maximum": 30000,
+ "minimum": 1000,
+ "type": "number"
+ },
+ "ev": {
+ "description": "An electric car is (or will be) charged at home: by default 15,000 km a year, about 2,955 kWh, charged in the evening (edit_roof device sets km, when and how fast).",
+ "type": "boolean"
+ },
+ "heat_pump": {
+ "description": "A heat pump heats the house: by default about 4,500 kWh a year, mostly in winter (edit_roof device sets the person's own figure).",
+ "type": "boolean"
+ },
+ "offer_eur": {
+ "description": "The total price on an offer the person already has, euros before any subsidy (0 clears it): the payback then counts it. Without one the payback counts the price guide's recommendation (summary.price_guide). Never pass a price of Sky Solar's as an offer.",
+ "maximum": 300000,
+ "minimum": 0,
+ "type": "number"
+ },
+ "optimizers": {
+ "description": "The person wants module optimizers: they are in both prices of the price guide. Without them the recommendation adds them anyway where the plan shows shade or several directions.",
+ "type": "boolean"
+ },
+ "roof_step": {
+ "description": "How demanding the roof is for the price guide: \"simple\" (one field, plain tiles), \"normal\", \"complex\" (several directions, many small fields, Biberschwanz tiles); \"auto\" (the default) works it out from the plan. Changes the price guide only.",
+ "enum": [
+ "auto",
+ "simple",
+ "normal",
+ "complex"
+ ],
+ "type": "string"
+ },
+ "type": {
+ "const": "household",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "How big the system is (the page's \"− 24 Module +\"). One fixed order through the modules: the sun sets it, only whole rows and columns count, east and west stay mixed, north comes last. Hand modules, modules taken out and faces switched off stay as they are. A first plan shows the whole roof; when the household's use is given the plan jumps to the target by itself, unless the person chose a size. Give modules or to, not both.",
+ "properties": {
+ "modules": {
+ "description": "How many modules: the nearest step of the plan's size ladder (summary.size.steps lists them). Each step is a tidy field, never modules scattered about.",
+ "maximum": 600,
+ "minimum": 1,
+ "type": "integer"
+ },
+ "to": {
+ "description": "\"target\": the size that suits the household (a year's yield 1.5 to 2 times its use, e-car and heat pump in), and it follows the household from then on; \"whole\": the whole roof; \"smaller\", \"larger\": one step along the ladder, as the page's − and +.",
+ "enum": [
+ "target",
+ "whole",
+ "smaller",
+ "larger"
+ ],
+ "type": "string"
+ },
+ "type": {
+ "const": "size",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "An e-car, a heat pump, its heating rod or a shiftable appliance with its own load curve: on or off, its settings and its colour (also the battery's colour). The car and the heat pump add to the household's day; appliances and the rod add no kWh, they give a share of the year a shape of its own. The answer's summary.devices shows each one's kWh a year and how much of it the roof covers; summary.device_colours the colours.",
+ "properties": {
+ "bivalence_c": {
+ "description": "heating_rod: the outdoor temperature below which it helps, °C (default -5).",
+ "maximum": 5,
+ "minimum": -20,
+ "type": "number"
+ },
+ "charge_kw": {
+ "description": "ev: charging power, kW: 2.3, 3.7, 7.4, 11, 22 (default 11, a usual wallbox).",
+ "type": "number"
+ },
+ "charges_per_week": {
+ "description": "ev: charging sessions a week, 1 to 7 (default 7, every day); each takes what built up since the last. Not for \"weekend\".",
+ "maximum": 7,
+ "minimum": 1,
+ "type": "integer"
+ },
+ "charging": {
+ "description": "ev: when the car is at home to charge: \"evening\" (default, from 18:00), \"daytime\" (home office or retired, from 11:00), \"weekend\" (Saturday and Sunday from 11:00). It charges at full power until done; no smart charging yet.",
+ "enum": [
+ "evening",
+ "daytime",
+ "weekend"
+ ],
+ "type": "string"
+ },
+ "colour": {
+ "description": "The device's colour as the 3D view draws it by the house (changes no figure): heat_pump and battery silver, white, anthracite, black (default silver); ev (the car's paint) silver, white, anthracite, black, blue, red (default silver). A colour alone does not count as an answer about km or kWh.",
+ "enum": [
+ "silver",
+ "white",
+ "anthracite",
+ "black",
+ "blue",
+ "red"
+ ],
+ "type": "string"
+ },
+ "device": {
+ "description": "\"ev\": an electric car charged at home (with its wallbox); \"heat_pump\": an air-to-water heat pump heating the house and its water; \"heating_rod\": the heat pump's electric rod (needs the heat pump; off by default); \"battery\": the home battery, only its colour (its size is household.battery_kwh, its type household.battery_type); \"dishwasher\", \"washer\", \"dryer\": a shiftable appliance run at a set time. An appliance adds nothing to the household's kWh a year: it is already inside them and moves out of the smooth curve into a timed shape. Fridge, freezer and TV stay in the base load.",
+ "enum": [
+ "ev",
+ "heat_pump",
+ "heating_rod",
+ "battery",
+ "dishwasher",
+ "washer",
+ "dryer"
+ ],
+ "type": "string"
+ },
+ "hot_water_percent": {
+ "description": "heating_rod: share of the hot water it heats instead of the heat pump, % (default 0), each morning. The rod's kWh stay inside the heat pump's kWh a year.",
+ "maximum": 100,
+ "minimum": 0,
+ "type": "number"
+ },
+ "km_per_year": {
+ "description": "ev: km a year charged at home (default 15000); at 19.7 kWh per 100 km (ADAC Ecotest 2023 average, at the socket).",
+ "maximum": 100000,
+ "minimum": 1000,
+ "type": "number"
+ },
+ "kwh_per_run": {
+ "description": "appliance: kWh per run (defaults: dishwasher 0.85, washer 0.8, dryer 1.5; EU label and Verbraucherzentrale figures; a dryer without a heat pump about 3.4).",
+ "maximum": 6,
+ "minimum": 0.1,
+ "type": "number"
+ },
+ "kwh_per_year": {
+ "anyOf": [
+ {
+ "maximum": 30000,
+ "minimum": 500,
+ "type": "number"
+ },
+ {
+ "const": "estimate",
+ "type": "string"
+ }
+ ],
+ "description": "heat_pump: its electricity a year in kWh (its meter, the bill or the installer's figure), or \"estimate\" for ours (4,500 kWh). Spread over the year by the measured outdoor temperature at the house."
+ },
+ "on": {
+ "description": "false takes the device out (its settings go with it); true or left out puts it in. Sending a device edit, even with no settings, records that the person answered for it (the defaults are right), so ask_the_person stops asking.",
+ "type": "boolean"
+ },
+ "peak_kw": {
+ "description": "appliance: kW of its heating peak (defaults: dishwasher 2, washer 2, dryer 0.8).",
+ "maximum": 4,
+ "minimum": 0.1,
+ "type": "number"
+ },
+ "peak_minutes": {
+ "description": "appliance: how long the peak lasts, minutes, all of it together (defaults: dishwasher 20, washer 15, dryer 100).",
+ "maximum": 240,
+ "minimum": 5,
+ "type": "number"
+ },
+ "rod_kw": {
+ "description": "heating_rod: its power, kW (default 6; rods have 3 to 9 kW).",
+ "maximum": 12,
+ "minimum": 1,
+ "type": "number"
+ },
+ "runs_per_week": {
+ "description": "appliance: runs a week, evenly over the week (defaults: dishwasher 5.4, washer 4.2, dryer 2).",
+ "maximum": 14,
+ "minimum": 0.5,
+ "type": "number"
+ },
+ "start": {
+ "description": "appliance: when it starts, \"HH:MM\" (to ten minutes) or \"sun\" for the midday window, a fixed time as a delay timer sets it (dishwasher 12:00, washer 11:00, dryer 14:00). Defaults are evening: dishwasher 20:00, washer 18:00, dryer 20:00.",
+ "pattern": "^(sun|([01]?\\d|2[0-3]):[0-5]\\d)$",
+ "type": "string"
+ },
+ "type": {
+ "const": "device",
+ "type": "string"
+ },
+ "wallbox_colour": {
+ "description": "ev: the wallbox's colour, white, silver, anthracite, black (default white).",
+ "enum": [
+ "white",
+ "silver",
+ "anthracite",
+ "black"
+ ],
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "device"
+ ],
+ "type": "object"
+ }
+]