"""Create a PowerPoint presentation for the solar system electrical plan — v2 (Series topology).""" from pptx import Presentation from pptx.util import Inches, Pt, Cm, Emu from pptx.dml.color import RGBColor from pptx.enum.text import PP_ALIGN, MSO_ANCHOR from pptx.enum.shapes import MSO_SHAPE from pptx.oxml.ns import qn import os prs = Presentation() prs.slide_width = Inches(13.333) prs.slide_height = Inches(7.5) # ── Color palette ── NAVY = RGBColor(0x0C, 0x23, 0x40) BLUE = RGBColor(0x1A, 0x52, 0x76) GOLD = RGBColor(0xF0, 0xC0, 0x40) WHITE = RGBColor(0xFF, 0xFF, 0xFF) LIGHT_BG = RGBColor(0xF5, 0xF6, 0xFA) RED = RGBColor(0xC6, 0x28, 0x28) ORANGE = RGBColor(0xE6, 0x51, 0x00) GREEN = RGBColor(0x2E, 0x7D, 0x32) TEAL = RGBColor(0x00, 0x89, 0x7B) DARK_TEXT = RGBColor(0x1A, 0x1A, 0x2E) GRAY = RGBColor(0x55, 0x55, 0x55) LIGHT_GOLD = RGBColor(0xFF, 0xF8, 0xE1) LIGHT_RED = RGBColor(0xFF, 0xEB, 0xEE) LIGHT_GREEN = RGBColor(0xE8, 0xF5, 0xE9) LIGHT_BLUE = RGBColor(0xE3, 0xF2, 0xFD) LIGHT_ORANGE = RGBColor(0xFF, 0xF3, 0xE0) PURPLE = RGBColor(0x7B, 0x5E, 0xA7) # ── Helpers ── def set_slide_bg(slide, color): bg = slide.background fill = bg.fill fill.solid() fill.fore_color.rgb = color def add_shape(slide, left, top, width, height, fill_color=None, line_color=None, shape_type=MSO_SHAPE.ROUNDED_RECTANGLE): shape = slide.shapes.add_shape(shape_type, left, top, width, height) shape.shadow.inherit = False if fill_color: shape.fill.solid() shape.fill.fore_color.rgb = fill_color else: shape.fill.background() if line_color: shape.line.color.rgb = line_color shape.line.width = Pt(1) else: shape.line.fill.background() return shape def set_text(shape, text, font_size=14, color=DARK_TEXT, bold=False, alignment=PP_ALIGN.RIGHT, font_name='Arial'): tf = shape.text_frame tf.word_wrap = True tf.auto_size = None p = tf.paragraphs[0] p.alignment = alignment run = p.add_run() run.text = text run.font.size = Pt(font_size) run.font.color.rgb = color run.font.bold = bold run.font.name = font_name pPr = p._p.get_or_add_pPr() pPr.set('rtl', '1') return tf def add_text_box(slide, left, top, width, height, text, font_size=14, color=DARK_TEXT, bold=False, alignment=PP_ALIGN.RIGHT): txBox = slide.shapes.add_textbox(left, top, width, height) tf = txBox.text_frame tf.word_wrap = True p = tf.paragraphs[0] p.alignment = alignment pPr = p._p.get_or_add_pPr() pPr.set('rtl', '1') run = p.add_run() run.text = text run.font.size = Pt(font_size) run.font.color.rgb = color run.font.bold = bold run.font.name = 'Arial' return tf def add_multiline_box(slide, left, top, width, height, lines, default_size=13, default_color=DARK_TEXT): txBox = slide.shapes.add_textbox(left, top, width, height) tf = txBox.text_frame tf.word_wrap = True for i, line_data in enumerate(lines): text = line_data[0] size = line_data[1] if len(line_data) > 1 else default_size color = line_data[2] if len(line_data) > 2 else default_color bold = line_data[3] if len(line_data) > 3 else False align = line_data[4] if len(line_data) > 4 else PP_ALIGN.RIGHT if i == 0: p = tf.paragraphs[0] else: p = tf.add_paragraph() p.alignment = align pPr = p._p.get_or_add_pPr() pPr.set('rtl', '1') p.space_after = Pt(4) run = p.add_run() run.text = text run.font.size = Pt(size) run.font.color.rgb = color run.font.bold = bold run.font.name = 'Arial' return tf def add_table_slide(slide, left, top, width, row_height, headers, rows, header_bg=NAVY, header_fg=WHITE, highlight_rows=None, col_ratios=None): num_rows = len(rows) + 1 num_cols = len(headers) table_shape = slide.shapes.add_table(num_rows, num_cols, left, top, width, Pt(row_height * num_rows)) table = table_shape.table if col_ratios: total = sum(col_ratios) for i, ratio in enumerate(col_ratios): table.columns[i].width = int(width * ratio / total) else: col_width = int(width / num_cols) for i in range(num_cols): table.columns[i].width = col_width for i, h in enumerate(headers): cell = table.cell(0, i) cell.text = h cell.fill.solid() cell.fill.fore_color.rgb = header_bg for p in cell.text_frame.paragraphs: p.alignment = PP_ALIGN.RIGHT pPr = p._p.get_or_add_pPr() pPr.set('rtl', '1') for run in p.runs: run.font.size = Pt(12) run.font.color.rgb = header_fg run.font.bold = True run.font.name = 'Arial' for r_idx, row in enumerate(rows): for c_idx, val in enumerate(row): cell = table.cell(r_idx + 1, c_idx) cell.text = str(val) if highlight_rows and r_idx in highlight_rows: cell.fill.solid() cell.fill.fore_color.rgb = LIGHT_GOLD for p in cell.text_frame.paragraphs: p.alignment = PP_ALIGN.RIGHT pPr = p._p.get_or_add_pPr() pPr.set('rtl', '1') for run in p.runs: run.font.size = Pt(11) run.font.color.rgb = DARK_TEXT run.font.name = 'Arial' return table def add_card(slide, left, top, width, height, title, body_lines, fill=WHITE, accent=GOLD): bar = add_shape(slide, left, top, width, Pt(4), fill_color=accent, shape_type=MSO_SHAPE.RECTANGLE) card = add_shape(slide, left, top + Pt(4), width, height - Pt(4), fill_color=fill, line_color=RGBColor(0xE0, 0xE4, 0xEA)) add_text_box(slide, left + Pt(10), top + Pt(10), width - Pt(20), Pt(24), title, font_size=15, color=NAVY, bold=True) add_multiline_box(slide, left + Pt(10), top + Pt(36), width - Pt(20), height - Pt(46), body_lines, default_size=11) def section_header(slide, text, y=Inches(0.3)): add_text_box(slide, Inches(0.5), y, Inches(12), Inches(0.7), text, font_size=28, color=NAVY, bold=True) add_shape(slide, Inches(0.5), y + Inches(0.65), Inches(2), Pt(3), fill_color=GOLD, shape_type=MSO_SHAPE.RECTANGLE) def add_block(slide, x, y, w, h, text, sub, fill, text_color=WHITE, sub_color=None): shape = add_shape(slide, x, y, w, h, fill_color=fill) tf = shape.text_frame tf.word_wrap = True tf.paragraphs[0].alignment = PP_ALIGN.CENTER pPr = tf.paragraphs[0]._p.get_or_add_pPr() pPr.set('rtl', '1') run = tf.paragraphs[0].add_run() run.text = text run.font.size = Pt(12) run.font.color.rgb = text_color run.font.bold = True run.font.name = 'Arial' if sub: p2 = tf.add_paragraph() p2.alignment = PP_ALIGN.CENTER pPr2 = p2._p.get_or_add_pPr() pPr2.set('rtl', '1') run2 = p2.add_run() run2.text = sub run2.font.size = Pt(9) run2.font.color.rgb = sub_color or RGBColor(0xCC, 0xCC, 0xCC) run2.font.name = 'Arial' def add_checklist_col(slide, x_start, y_start, items): """items = list of (task, detail)""" y = y_start for task, detail in items: add_text_box(slide, x_start, y, Inches(5.5), Inches(0.2), task, font_size=11, color=DARK_TEXT, bold=True) add_text_box(slide, x_start, y + Pt(14), Inches(5.5), Inches(0.15), detail, font_size=9, color=GRAY) y += Inches(0.4) # ═══════════════════════════════════════════════════════════ # SLIDE 1 — Title # ═══════════════════════════════════════════════════════════ slide = prs.slides.add_slide(prs.slide_layouts[6]) set_slide_bg(slide, NAVY) add_shape(slide, Inches(1), Inches(3.1), Inches(2), Pt(3), fill_color=GOLD, shape_type=MSO_SHAPE.RECTANGLE) add_text_box(slide, Inches(1), Inches(1.2), Inches(11), Inches(1.5), 'תכנית חשמלית — מערכת סולארית היברידית', font_size=40, color=WHITE, bold=True) add_text_box(slide, Inches(1), Inches(2.4), Inches(11), Inches(0.6), 'חוות יאיר — אלי ספרא', font_size=24, color=RGBColor(0xA8, 0xD8, 0xEA)) meta_items = [ 'מרץ 2026', 'ממיר: Solis S6-EH3P20K-H | 20kW תלת-פאזי', 'סוללה: CNTE 18.8kWh HV | LiFePO4', 'פאנלים: 18 x 620W = 11.16kWp', 'Zero Export | טופולוגיה סדרתית | ללא CT', ] add_multiline_box(slide, Inches(1), Inches(3.5), Inches(11), Inches(2.5), [(m, 16, RGBColor(0xCD, 0xDE, 0xEE)) for m in meta_items]) # Bottom bar add_shape(slide, Inches(0), Inches(7.1), Inches(13.333), Pt(30), fill_color=GOLD, shape_type=MSO_SHAPE.RECTANGLE) add_text_box(slide, Inches(1), Inches(7.12), Inches(11), Pt(26), 'תכנון בלבד — לאישור חשמלאי מוסמך לפני ביצוע', font_size=12, color=NAVY, bold=True, alignment=PP_ALIGN.CENTER) # ═══════════════════════════════════════════════════════════ # SLIDE 2 — Notes (replacing Critical Warnings) # ═══════════════════════════════════════════════════════════ slide = prs.slides.add_slide(prs.slide_layouts[6]) set_slide_bg(slide, LIGHT_BG) add_text_box(slide, Inches(0.5), Inches(0.3), Inches(12), Inches(0.7), 'הערות חשובות לפני ביצוע', font_size=28, color=ORANGE, bold=True) add_shape(slide, Inches(0.5), Inches(0.95), Inches(2), Pt(3), fill_color=ORANGE, shape_type=MSO_SHAPE.RECTANGLE) notes = [ ('טופולוגיה סדרתית (Series)', 'כל החשמל עובר דרך הממיר. אין CT. אין DC Isolators חיצוניים\n(מובנים בממיר ובסוללה). מפסקי C40 מספיקים.', 'הוודא על ידי המתקין'), ('RCD (פחת)', 'לא נכלל כרגע — ייקבע לפי דרישת הבודק בהתאם למדדי הארקה.\nמומלץ לשקול הוספה מראש לבטיחות.', 'נדחה לשלב בדיקה'), ('טבעת מגנטית לכבל סוללה', 'לפי המתקין, כבל הסוללה קצר מדי ללפף.\nנקודה פתוחה — לברר אם CNTE כולל ferrite מובנה, או להשתמש בכבל ארוך יותר.', 'נקודה פתוחה'), ] y = Inches(1.3) for title, desc, status in notes: card = add_shape(slide, Inches(0.5), y, Inches(12), Inches(1.4), fill_color=WHITE, line_color=RGBColor(0xE0, 0xA0, 0x00)) add_shape(slide, Inches(0.5), y, Pt(6), Inches(1.4), fill_color=ORANGE, shape_type=MSO_SHAPE.RECTANGLE) add_text_box(slide, Inches(0.8), y + Pt(8), Inches(9), Pt(24), title, font_size=16, color=ORANGE, bold=True) add_text_box(slide, Inches(0.8), y + Pt(34), Inches(9), Pt(60), desc, font_size=12, color=GRAY) # Status badge on the right badge = add_shape(slide, Inches(10), y + Pt(14), Inches(2.2), Inches(0.35), fill_color=LIGHT_GOLD) set_text(badge, status, font_size=11, color=ORANGE, bold=True, alignment=PP_ALIGN.CENTER) y += Inches(1.6) # Key info box box = add_shape(slide, Inches(0.5), Inches(6), Inches(12), Inches(0.8), fill_color=LIGHT_GREEN) add_multiline_box(slide, Inches(0.7), Inches(6.05), Inches(11.6), Inches(0.7), [ ('שינויים עיקריים מגרסה קודמת: ללא DC Isolators חיצוניים (מובנים) | ללא CT (series topology) | C40 מספיק (לא C50) | Zero Export אוטומטי | 3P אחרי בורר מצבים', 12, GREEN, True, PP_ALIGN.CENTER), ]) # ═══════════════════════════════════════════════════════════ # SLIDE 3 — Single Line Diagram # ═══════════════════════════════════════════════════════════ slide = prs.slides.add_slide(prs.slide_layouts[6]) set_slide_bg(slide, LIGHT_BG) section_header(slide, '1. תרשים חד-קווי (Single Line Diagram)') # Series topology banner banner = add_shape(slide, Inches(0.5), Inches(1.1), Inches(12.3), Inches(0.4), fill_color=LIGHT_GREEN) add_text_box(slide, Inches(0.7), Inches(1.12), Inches(12), Inches(0.35), 'טופולוגיה סדרתית — כל החשמל עובר דרך הממיר | Zero Export | ללא CT', font_size=13, color=GREEN, bold=True, alignment=PP_ALIGN.CENTER) LINE_W = Pt(2.5) def add_line_h(slide, x, y, length, color): """Horizontal line (thin rectangle).""" add_shape(slide, x, y, length, LINE_W, fill_color=color, shape_type=MSO_SHAPE.RECTANGLE) def add_line_v(slide, x, y, length, color): """Vertical line (thin rectangle).""" add_shape(slide, x, y, LINE_W, length, fill_color=color, shape_type=MSO_SHAPE.RECTANGLE) DC_PV_COLOR = RGBColor(0xF0, 0xC0, 0x40) DC_BATT_COLOR = TEAL AC_GRID_COLOR = RGBColor(0x15, 0x65, 0xC0) AC_BACKUP_COLOR = ORANGE AC_DIST_COLOR = RGBColor(0x45, 0x5A, 0x64) BMS_COLOR = RGBColor(0x26, 0xA6, 0x9A) # ── Block positions (all in inches, stored for line drawing) ── # PV Strings s1_x, s1_y, s1_w, s1_h = 2.0, 1.7, 2.5, 0.65 s2_x, s2_y, s2_w, s2_h = 8.5, 1.7, 2.5, 0.65 # Inverter inv_x, inv_y, inv_w, inv_h = 4.5, 2.85, 4.2, 0.9 # Battery bat_x, bat_y, bat_w, bat_h = 0.5, 2.95, 2.3, 0.8 # SPD spd_x, spd_y, spd_w, spd_h = 5.1, 4.1, 1.8, 0.5 # MCB mcb_x, mcb_y, mcb_w, mcb_h = 7.2, 4.1, 1.5, 0.5 # Changeover chg_x, chg_y, chg_w, chg_h = 5.4, 4.85, 2.5, 0.55 # Grid grid_x, grid_y, grid_w, grid_h = 2.5, 5.75, 2.0, 0.55 # Load load_x, load_y, load_w, load_h = 8.5, 5.75, 2.0, 0.55 # Backup bkp_x, bkp_y, bkp_w, bkp_h = 10.5, 2.95, 2.2, 0.8 # ── Draw blocks ── str_w_in = Inches(s1_w) add_block(slide, Inches(s1_x), Inches(s1_y), str_w_in, Inches(s1_h), 'String 1: 9x620W = 5,580Wp', 'Voc≈396V | Isc≈18A | MPPT1', DC_PV_COLOR, DARK_TEXT, GRAY) add_block(slide, Inches(s2_x), Inches(s2_y), str_w_in, Inches(s2_h), 'String 2: 9x620W = 5,580Wp', 'Voc≈396V | Isc≈18A | MPPT2', DC_PV_COLOR, DARK_TEXT, GRAY) add_text_box(slide, Inches(4.5), Inches(2.45), Inches(4), Inches(0.25), 'DC switch מובנה בממיר — ללא DC Isolator חיצוני', font_size=10, color=GRAY, alignment=PP_ALIGN.CENTER) add_block(slide, Inches(inv_x), Inches(inv_y), Inches(inv_w), Inches(inv_h), 'Solis S6-EH3P20K-H', 'Hybrid 20kW | 3φ | IP66 | Series | DC switch + AFCI מובנים', NAVY, WHITE, RGBColor(0xA8, 0xD8, 0xEA)) add_block(slide, Inches(bat_x), Inches(bat_y), Inches(bat_w), Inches(bat_h), 'CNTE 18.8kWh', 'HV | LFP | IP66 | DC switch מובנה', RGBColor(0xE0, 0xF2, 0xF1), DARK_TEXT, GRAY) add_block(slide, Inches(spd_x), Inches(spd_y), Inches(spd_w), Inches(spd_h), 'SPD Type 2', '3P+N | 40kA', LIGHT_RED, RED, GRAY) add_block(slide, Inches(mcb_x), Inches(mcb_y), Inches(mcb_w), Inches(mcb_h), 'MCB C40 4P', 'יציאת ממיר', RGBColor(0xED, 0xE7, 0xF6), DARK_TEXT, GRAY) add_block(slide, Inches(chg_x), Inches(chg_y), Inches(chg_w), Inches(chg_h), 'בורר מצבים Hager 4P 40A', 'Grid / Solar / Off', RGBColor(0xF3, 0xE5, 0xF5), DARK_TEXT, GRAY) add_block(slide, Inches(grid_x), Inches(grid_y), Inches(grid_w), Inches(grid_h), 'MCB C40 3P → מונה יישוב', 'רשת חשמל', RGBColor(0xEC, 0xEF, 0xF1), DARK_TEXT, GRAY) add_block(slide, Inches(load_x), Inches(load_y), Inches(load_w), Inches(load_h), 'MCB C40 3P → לוח חשמלי', 'עומסי הבית', LIGHT_GREEN, DARK_TEXT, GRAY) add_block(slide, Inches(bkp_x), Inches(bkp_y), Inches(bkp_w), Inches(bkp_h), 'עומסים חיוניים', 'Backup UPS <10ms | C40 3P', LIGHT_ORANGE, DARK_TEXT, GRAY) # ── Draw connecting lines ── # String 1 bottom center → down to merge line s1_cx = s1_x + s1_w / 2 # 3.25 s1_bot = s1_y + s1_h # 2.35 add_line_v(slide, Inches(s1_cx), Inches(s1_bot), Inches(0.25), DC_PV_COLOR) # String 2 bottom center → down to merge line s2_cx = s2_x + s2_w / 2 # 9.75 s2_bot = s2_y + s2_h # 2.35 add_line_v(slide, Inches(s2_cx), Inches(s2_bot), Inches(0.25), DC_PV_COLOR) # Horizontal merge line between String 1 and String 2 at y=2.6 merge_y = 2.6 add_line_h(slide, Inches(s1_cx), Inches(merge_y), Inches(s2_cx - s1_cx), DC_PV_COLOR) # Center of merge → down to inverter top inv_cx = inv_x + inv_w / 2 # 6.6 add_line_v(slide, Inches(inv_cx), Inches(merge_y), Inches(inv_y - merge_y), DC_PV_COLOR) # Battery right edge → horizontal to inverter left edge (DC HV) bat_right = bat_x + bat_w # 2.8 bat_cy = bat_y + bat_h / 2 # 3.35 add_line_h(slide, Inches(bat_right), Inches(bat_cy), Inches(inv_x - bat_right), DC_BATT_COLOR) # BMS label add_text_box(slide, Inches(3.0), Inches(3.05), Inches(1.3), Inches(0.2), 'DC HV + CAN/BMS', font_size=9, color=BMS_COLOR, alignment=PP_ALIGN.CENTER) # ── AC path: Inverter → SPD → MCB → Changeover ── inv_bot = inv_y + inv_h # 3.75 spd_cx = spd_x + spd_w / 2 # 6.0 spd_cy = spd_y + spd_h / 2 # 4.35 spd_right = spd_x + spd_w # 6.9 mcb_cx = mcb_x + mcb_w / 2 # 7.95 mcb_bot = mcb_y + mcb_h # 4.6 chg_cx = chg_x + chg_w / 2 # 6.65 chg_top = chg_y # 4.85 # 1. Inv center (6.60) down short elbow_y = inv_bot + 0.08 # 3.83 add_line_v(slide, Inches(inv_cx), Inches(inv_bot), Inches(elbow_y - inv_bot), AC_GRID_COLOR) # 2. Horizontal jog from inv_cx (6.60) to spd_cx (6.00) add_line_h(slide, Inches(spd_cx), Inches(elbow_y), Inches(inv_cx - spd_cx), AC_GRID_COLOR) # 3. Down from spd_cx to SPD top (6.00, 4.10) add_line_v(slide, Inches(spd_cx), Inches(elbow_y), Inches(spd_y - elbow_y), AC_GRID_COLOR) # 4. SPD right → MCB left add_line_h(slide, Inches(spd_right), Inches(spd_cy), Inches(mcb_x - spd_right), AC_GRID_COLOR) # 5. MCB bottom center → down elbow2_y = mcb_bot + 0.05 # 4.65 add_line_v(slide, Inches(mcb_cx), Inches(mcb_bot), Inches(elbow2_y - mcb_bot), AC_GRID_COLOR) # 6. Horizontal from mcb_cx to chg_cx add_line_h(slide, Inches(chg_cx), Inches(elbow2_y), Inches(mcb_cx - chg_cx), AC_GRID_COLOR) # 7. Down from chg_cx to changeover top add_line_v(slide, Inches(chg_cx), Inches(elbow2_y), Inches(chg_top - elbow2_y), AC_GRID_COLOR) # Changeover bottom → down then split left/right chg_bot = chg_y + chg_h # 5.4 branch_y = 5.55 add_line_v(slide, Inches(chg_cx), Inches(chg_bot), Inches(branch_y - chg_bot), AC_DIST_COLOR) # Horizontal branch line grid_cx = grid_x + grid_w / 2 # 3.5 load_cx = load_x + load_w / 2 # 9.5 add_line_h(slide, Inches(grid_cx), Inches(branch_y), Inches(load_cx - grid_cx), AC_DIST_COLOR) # Grid branch: down from branch line to grid block add_line_v(slide, Inches(grid_cx), Inches(branch_y), Inches(grid_y - branch_y), AC_DIST_COLOR) # Load branch: down from branch line to load block add_line_v(slide, Inches(load_cx), Inches(branch_y), Inches(load_y - branch_y), AC_DIST_COLOR) # Backup: Inverter right → horizontal to backup block left inv_right = inv_x + inv_w # 8.7 inv_cy = inv_y + inv_h / 2 # 3.3 bkp_left = bkp_x # 10.5 bkp_cy = bkp_y + bkp_h / 2 # 3.35 # Horizontal line add_line_h(slide, Inches(inv_right), Inches(inv_cy), Inches(bkp_left - inv_right), AC_BACKUP_COLOR) # Backup label add_text_box(slide, Inches(inv_right + 0.1), Inches(inv_cy - 0.25), Inches(1.5), Inches(0.2), 'Backup AC | UPS <10ms', font_size=9, color=AC_BACKUP_COLOR, alignment=PP_ALIGN.CENTER) # ── Legend with colored line samples ── legend_y = Inches(6.55) legend_items = [ (0.5, DC_PV_COLOR, 'DC PV'), (2.2, DC_BATT_COLOR, 'DC Battery'), (4.2, AC_GRID_COLOR, 'AC Grid'), (6.0, AC_BACKUP_COLOR, 'AC Backup'), (8.0, AC_DIST_COLOR, 'Distribution'), (10.2, BMS_COLOR, 'BMS'), ] legend_bg = add_shape(slide, Inches(0.3), legend_y - Pt(4), Inches(12.7), Inches(0.4), fill_color=RGBColor(0xF5, 0xF5, 0xF5), line_color=RGBColor(0xE0, 0xE0, 0xE0)) for lx, lcolor, ltext in legend_items: add_shape(slide, Inches(lx), legend_y + Pt(4), Inches(0.4), Pt(3), fill_color=lcolor, shape_type=MSO_SHAPE.RECTANGLE) add_text_box(slide, Inches(lx + 0.45), legend_y - Pt(2), Inches(1.3), Inches(0.3), ltext, font_size=10, color=GRAY, alignment=PP_ALIGN.LEFT) # ═══════════════════════════════════════════════════════════ # SLIDE 4 — Component List (10 items) # ═══════════════════════════════════════════════════════════ slide = prs.slides.add_slide(prs.slide_layouts[6]) set_slide_bg(slide, LIGHT_BG) section_header(slide, '2. רשימת רכיבים מפורטת') components = [ ('1', 'פאנלים סולאריים', '620W — מק"ט s620', '18', 'גג', '2 סטרינגים x 9. DC switch מובנה בממיר'), ('2', 'ממיר היברידי', 'Solis S6-EH3P20K-H', '1', 'קיר מוצל', 'IP66. DC switch + AFCI מובנים'), ('3', 'סוללה', 'CNTE 18.8kWh HV', '1', 'ליד הממיר', 'IP66. DC switch מובנה. CAN'), ('4', 'מגן ברקים SPD', 'Type 2, 3P+N, 40kA', '1', 'יציאת AC ממיר', 'הגנת AC'), ('5', 'מפסק יציאת ממיר', 'ABB C40 4P', '1', 'אחרי SPD', '4P — כולל נייטרל'), ('6', 'בורר מצבים', 'Hager 4P 40A', '1', 'לפני התפצלות', 'Grid / Solar / Off'), ('7', 'מפסק רשת', 'ABB C40 3P', '1', 'ענף רשת', '3P — אחרי בורר'), ('8', 'מפסק עומסים', 'ABB C40 3P', '1', 'ענף עומסים', '3P — אחרי בורר'), ('9', 'מפסק גיבוי', 'ABB C40 3P', '1', 'Backup ממיר', '3P — עומסים חיוניים'), ('10', 'מונה יישוב', 'דו-כיווני', '1', 'לפני הרשת', 'מסופק ע"י יישוב'), ] add_table_slide(slide, Inches(0.3), Inches(1.2), Inches(12.7), 30, ['#', 'רכיב', 'דגם / מפרט', 'כמות', 'מיקום', 'הערות'], components, highlight_rows=[3], col_ratios=[0.5, 2, 2.5, 0.7, 1.5, 3]) # Note about what's NOT in the list note = add_shape(slide, Inches(0.3), Inches(5.8), Inches(12.7), Inches(0.7), fill_color=LIGHT_BLUE) add_multiline_box(slide, Inches(0.5), Inches(5.85), Inches(12.3), Inches(0.6), [ ('לא נדרשים: DC Isolators חיצוניים (מובנים בממיר ובסוללה) | CT חיישן זרם (Series topology = Zero Export אוטומטי) | C50 (C40 מספיק)', 12, BLUE, True, PP_ALIGN.CENTER), ]) # ═══════════════════════════════════════════════════════════ # SLIDE 5 — Cable Specifications # ═══════════════════════════════════════════════════════════ slide = prs.slides.add_slide(prs.slide_layouts[6]) set_slide_bg(slide, LIGHT_BG) section_header(slide, '3. מפרט כבילה') cables = [ ('PV String → Inverter', 'H1Z2Z2-K סולארי', '6mm²', 'לפי מיקום גג', 'ישירות לממיר, MC4, UV-resistant'), ('Inverter ↔ Battery', 'DC גמיש', '16mm²', '~3m', 'מגיע עם הציוד. ⚠ ferrite — נקודה פתוחה'), ('Inverter → SPD → MCB', 'NYY-J 5G AC', '10mm²', '~5m', '3L + N + PE'), ('MCB → Switch → Grid/Load', 'NYY-J 5G AC', '10mm²', '~10m', 'לפי מרחק ללוח'), ('Backup → MCB → Essential', 'NYY-J 5G AC', '6mm²', '~8m', '3P עומסים חיוניים'), ('הארקה', 'ירוק-צהוב', '16mm² נחושת', '—', 'ממיר+סוללה+מבנה'), ('BMS Communication', 'CAN מסוכך', 'שזור', '~3m', 'מגיע מלופף מראש בטבעת מגנטית'), ] add_table_slide(slide, Inches(0.5), Inches(1.2), Inches(12.3), 30, ['קטע', 'סוג כבל', 'חתך', 'אורך', 'הערות'], cables, highlight_rows=[1], col_ratios=[2.5, 2, 1.2, 1, 3.5]) # Ferrite note note = add_shape(slide, Inches(0.5), Inches(4.5), Inches(12.3), Inches(0.9), fill_color=LIGHT_GOLD) add_multiline_box(slide, Inches(0.7), Inches(4.55), Inches(12), Inches(0.8), [ ('טבעות מגנטיות (Ferrite Rings):', 13, ORANGE, True), ('כבל CAN — מגיע מלופף מראש (לפי המתקין)', 12, DARK_TEXT), ('כבל DC סוללה — המתקין מציין שהוא קצר מדי ללפף. נקודה פתוחה — לברר מול CNTE אם יש ferrite מובנה, או לספק כבל ארוך יותר', 12, ORANGE, True), ]) # ═══════════════════════════════════════════════════════════ # SLIDE 6 — Installation Guidelines # ═══════════════════════════════════════════════════════════ slide = prs.slides.add_slide(prs.slide_layouts[6]) set_slide_bg(slide, LIGHT_BG) section_header(slide, '4. הנחיות התקנה') card_w = Inches(5.9) card_h = Inches(2.5) add_card(slide, Inches(0.5), Inches(1.2), card_w, card_h, 'מיקום ממיר וסוללה', [ ('• קיר מוצל — הגנה משמש ישירה', 12, DARK_TEXT), ('• גובה מינימלי 60 ס"מ מהקרקע', 12, DARK_TEXT), ('• מרווח אוורור: 30 ס"מ מצדדים, 50 ס"מ מלמעלה', 12, DARK_TEXT), ('• נגיש לתחזוקה', 12, DARK_TEXT), ('• IP66 — מתאים לחוץ', 12, GREEN), ], accent=BLUE) add_card(slide, Inches(6.8), Inches(1.2), card_w, card_h, 'הארקה', [ ('• גוף ממיר + גוף סוללה', 12, DARK_TEXT), ('• מבנה קונסטרוקציה', 12, DARK_TEXT), ('• מסגרות פאנלים', 12, DARK_TEXT), ('• חתך מינימלי: 16mm² נחושת', 12, DARK_TEXT), ('• פס הארקה → אלקטרודה', 12, DARK_TEXT), ], accent=GREEN) add_card(slide, Inches(0.5), Inches(4.0), card_w, card_h, 'חיבור סוללה', [ ('• כבל DC + כבל CAN מגיעים עם הציוד', 12, DARK_TEXT), ('• כבל CAN מלופף מראש בטבעת מגנטית', 12, DARK_TEXT), ('• כבל DC — קצר מדי ללפף (נקודה פתוחה)', 12, ORANGE, True), ('• מומנט הידוק: 24.5 N·m', 12, DARK_TEXT), ('• לוודא קוטביות לפני חיבור!', 12, RED, True), ], accent=TEAL) add_card(slide, Inches(6.8), Inches(4.0), card_w, card_h, 'אחריות ושירות', [ ('• Solis — RCS Solar בע"מ', 12, DARK_TEXT), ('• CNTE/Yoshopo — RCS Solar בע"מ', 12, DARK_TEXT), ('• 10 שנים על ממיר וסוללה', 12, DARK_TEXT), ('• התקנת פאנלים — באחריות הלקוח', 12, DARK_TEXT), ], accent=GOLD) # ═══════════════════════════════════════════════════════════ # SLIDE 7 — QA Open Items # ═══════════════════════════════════════════════════════════ slide = prs.slides.add_slide(prs.slide_layouts[6]) set_slide_bg(slide, LIGHT_BG) add_text_box(slide, Inches(0.5), Inches(0.3), Inches(12), Inches(0.7), '5. בקרת איכות — נקודות פתוחות', font_size=28, color=ORANGE, bold=True) add_shape(slide, Inches(0.5), Inches(0.95), Inches(2), Pt(3), fill_color=ORANGE, shape_type=MSO_SHAPE.RECTANGLE) open_items = [ ('Q1', 'RCD (פחת)', 'המתקין: לא שמים כרגע. יוסיפו RCBO אם הבודק ידרוש', 'נדחה לבדיקה. מומלץ מראש'), ('Q2', 'AFCI', 'מובנה בסוליס בצד DC, דורש הפעלה ידנית', 'לוודא activation בהתקנה'), ('Q3', 'SPD DC חסר', 'SPD בצד AC בלבד. פאנלים חשופים לברק בצד DC', 'לשקול SPD DC 1000Vdc'), ('Q4', 'Ferrite לכבל סוללה', 'כבל DC קצר מדי ללפף. CAN מלופף מראש', 'לברר מול CNTE — נקודה פתוחה'), ] add_table_slide(slide, Inches(0.5), Inches(1.2), Inches(12.3), 34, ['#', 'ממצא', 'מצב', 'פעולה נדרשת'], open_items, highlight_rows=[0, 1, 2, 3], col_ratios=[0.5, 1.5, 4, 3]) # ═══════════════════════════════════════════════════════════ # SLIDE 8 — QA Verified # ═══════════════════════════════════════════════════════════ slide = prs.slides.add_slide(prs.slide_layouts[6]) set_slide_bg(slide, LIGHT_BG) add_text_box(slide, Inches(0.5), Inches(0.3), Inches(12), Inches(0.7), '5. בקרת איכות — פריטים תקינים', font_size=28, color=GREEN, bold=True) add_shape(slide, Inches(0.5), Inches(0.95), Inches(2), Pt(3), fill_color=GREEN, shape_type=MSO_SHAPE.RECTANGLE) verified = [ ('טופולוגיה', 'סדרתית (Series) — אין CT. C40 מספיק', 'אושר ע"י מתקין ✓'), ('Voc סטרינג', '396V < 1000V, בתוך MPPT 200–850V', '✓ תקין'), ('Isc סטרינג', '~18A < 20A מקסימום', '✓ תקין'), ('התאמת סוללה', 'CNTE HV 120–800V = סוליס 120–800V', '✓ תקין'), ('זרם סוללה', '50A = סוליס 50A max', '✓ תקין'), ('IP Rating', 'ממיר IP66 + סוללה IP66', '✓ תקין'), ('תקשורת BMS', 'CAN/RS485 תואם', '✓ תקין'), ('DC Switch מובנה', 'ממיר + סוללה — לא צריך חיצוני', 'אושר ע"י מתקין ✓'), ('מפסקי C40', 'בסדרתי הממיר לא מושך מעבר לצריכה (3x40A)', 'אושר ע"י מתקין ✓'), ('קטבים', 'יציאת ממיר: 4P. אחרי בורר: 3P. Backup: 3P', 'אושר ע"י מתקין ✓'), ('Zero Export', 'בסדרתי — אוטומטי, ללא CT', 'אושר ע"י מתקין ✓'), ('טעינת סוללה', 'מסולארי בלבד — לא מהרשת', 'אושר ע"י מתקין ✓'), ] add_table_slide(slide, Inches(0.5), Inches(1.2), Inches(12.3), 28, ['פריט', 'בדיקה', 'תוצאה'], verified, col_ratios=[1.5, 4, 2]) # ═══════════════════════════════════════════════════════════ # SLIDE 9 — Execution Phase A+B # ═══════════════════════════════════════════════════════════ slide = prs.slides.add_slide(prs.slide_layouts[6]) set_slide_bg(slide, LIGHT_BG) section_header(slide, '6. הנחיות ביצוע — שלב א\'+ב\'') # Warning warn = add_shape(slide, Inches(0.5), Inches(1.1), Inches(12.3), Inches(0.5), fill_color=LIGHT_RED) add_text_box(slide, Inches(0.7), Inches(1.15), Inches(12), Inches(0.4), 'כל העבודה החשמלית חייבת להתבצע על ידי חשמלאי מוסמך. עבודה על DC גבוה (עד 800V) מסוכנת!', font_size=12, color=RED, bold=True) add_text_box(slide, Inches(0.5), Inches(1.7), Inches(6), Inches(0.4), 'שלב א\' — הכנה ורכש', font_size=16, color=NAVY, bold=True) add_checklist_col(slide, Inches(0.7), Inches(2.1), [ ('☐ אישור תכנית מול חשמלאי', 'התאמה לתקנות ולדרישות היישוב'), ('☐ רכש רכיבים חסרים', 'ABB C40 4P x1, ABB C40 3P x3, SPD AC Type 2, Hager 4P 40A'), ('☐ רכש כבלים', 'H1Z2Z2-K 6mm², NYY-J 5G10, הארקה 16mm². DC+CAN מגיעים עם הציוד'), ('☐ סימון מיקום', 'קיר מוצל, גובה 60+, אוורור 30/50 ס"מ, נגיש לתחזוקה'), ]) add_text_box(slide, Inches(6.8), Inches(1.7), Inches(6), Inches(0.4), 'שלב ב\' — התקנה מכנית', font_size=16, color=NAVY, bold=True) add_checklist_col(slide, Inches(7), Inches(2.1), [ ('☐ קונסטרוקציה על הגג', 'מסילות, יציבות רוח, שיפוע, אטימה'), ('☐ הרכבת 18 פאנלים', '2 סטרינגים x 9, חיבור טורי, הארקת מסגרות'), ('☐ התקנת ממיר', 'Solis S6-EH3P20K-H — תלייה + מפלס'), ('☐ התקנת סוללה', 'CNTE 18.8kWh — חיזוק קיר (~100 ק"ג)'), ('☐ לוח מפסקים', 'מפסקים, SPD, בורר מצבים. סימון ברור'), ]) # ═══════════════════════════════════════════════════════════ # SLIDE 10 — Execution Phase C+D # ═══════════════════════════════════════════════════════════ slide = prs.slides.add_slide(prs.slide_layouts[6]) set_slide_bg(slide, LIGHT_BG) section_header(slide, '6. הנחיות ביצוע — שלב ג\'+ד\'') add_text_box(slide, Inches(0.5), Inches(1.1), Inches(6), Inches(0.4), 'שלב ג\' — כבילה וחיבורים', font_size=16, color=NAVY, bold=True) add_checklist_col(slide, Inches(0.7), Inches(1.5), [ ('☐ DC פאנלים → ממיר', 'H1Z2Z2-K 6mm², MC4. ישירות לממיר. לוודא קוטביות!'), ('☐ DC סוללה', 'כבל מגיע עם הציוד. מומנט 24.5 N·m. בדיקת קוטביות!'), ('☐ BMS/CAN', 'מגיע מלופף מראש בטבעת מגנטית'), ('☐ AC Grid', 'NYY-J 5G10 → SPD → MCB C40 4P → בורר → התפצלות'), ('☐ AC Backup', 'NYY-J 5G10 → MCB C40 3P → עומסים חיוניים'), ('☐ חיבור רשת', 'בורר → MCB C40 3P → מונה → רשת'), ('☐ הארקה', '16mm² Cu: ממיר+סוללה+מבנה+פאנלים → אלקטרודה'), ]) add_text_box(slide, Inches(6.8), Inches(1.1), Inches(6), Inches(0.4), 'שלב ד\' — בדיקות לפני הפעלה', font_size=16, color=NAVY, bold=True) add_checklist_col(slide, Inches(7), Inches(1.5), [ ('☐ Voc סטרינג', '~396V ±5%, הפרש <5% בין סטרינגים'), ('☐ Isc סטרינג', '~18A בשמש מלאה, מודד DC rated'), ('☐ קוטביות DC', '+ ל-+, – ל-–. חיבור הפוך = הרס ממיר!'), ('☐ בידוד DC', 'מגר: >1MΩ בין +/– לאדמה'), ('☐ רציפות הארקה', '<0.5Ω מכל מסגרת לפס הארקה'), ('☐ מתח סוללה', '120–800V, SOC מינימלי ~20%'), ('☐ תקשורת BMS', 'ממיר מזהה סוללה, SOC/מתח/טמפ\''), ('☐ מפסקים', 'הפעלה/ניתוק ידני של כל מפסק'), ('☐ מתח AC', '~400V בין פאזות, ~230V פאזה-נייטרל'), ]) # ═══════════════════════════════════════════════════════════ # SLIDE 11 — Execution Phase E+F # ═══════════════════════════════════════════════════════════ slide = prs.slides.add_slide(prs.slide_layouts[6]) set_slide_bg(slide, LIGHT_BG) section_header(slide, '6. הנחיות ביצוע — שלב ה\'+ו\'') add_text_box(slide, Inches(0.5), Inches(1.1), Inches(6), Inches(0.4), 'שלב ה\' — הפעלה ראשונה (Commissioning)', font_size=16, color=NAVY, bold=True) add_checklist_col(slide, Inches(0.7), Inches(1.5), [ ('☐ הפעלה לפי סדר', '1. DC switch סוללה → 2. DC switch ממיר (PV) → 3. AC → 4. ממיר'), ('☐ הגדרות ממיר', 'BT+APP: סוג סוללה (Lithium/CAN), מצב: Zero Export, תדר, מתח'), ('☐ הפעלת AFCI', 'activation required — הגנת קשת DC'), ('☐ הגדרת Zero Export', 'וידוא שאין ייצוא לרשת. לוודא שממיר מוריד ייצור כשאין צריכה'), ('☐ בדיקת Backup', 'ניתוק רשת → עומסים חיוניים עובדים <10ms'), ('☐ WiFi/Ethernet', 'חיבור לניטור — SolisCloud'), ('☐ ניטור 24 שעות', 'ייצור, טעינה, צריכה, ייבוא. וידוא Zero Export!'), ]) add_text_box(slide, Inches(6.8), Inches(1.1), Inches(6), Inches(0.4), 'שלב ו\' — סימון ותיעוד', font_size=16, color=NAVY, bold=True) add_checklist_col(slide, Inches(7), Inches(1.5), [ ('☐ סימון מפסקים', '"PV 1", "PV 2", "סוללה", "AC", "רשת", "עומסים", "Backup"'), ('☐ שלט כיבוי חירום', '"1. כבה DC switch על ממיר וסוללה 2. נתק AC"'), ('☐ תיעוד', 'תכנית, אישורים, סריאליים, תמונות'), ('☐ מסירה ללקוח', 'הדרכה: אפליקציה, מצבים, חירום, תחזוקה'), ]) # Key differences box box = add_shape(slide, Inches(6.8), Inches(3.4), Inches(5.9), Inches(2.8), fill_color=WHITE, line_color=NAVY) add_multiline_box(slide, Inches(7), Inches(3.5), Inches(5.5), Inches(2.6), [ ('הבדלים מגרסה קודמת — כיבוי חירום:', 14, NAVY, True), ('', 6), ('הפעלה/כיבוי דרך DC switch מובנה', 13, DARK_TEXT, True), ('(לא DC Isolators חיצוניים)', 12, GRAY), ('', 6), ('1. כבה DC switch על הממיר', 13, RED, True), ('2. כבה DC switch על הסוללה', 13, RED, True), ('3. נתק מפסק AC', 13, RED, True), ('', 6), ('⚠ פאנלים ממשיכים לייצר מתח (~396V)', 12, RED), ('כל עוד יש אור — לא לגעת ב-MC4!', 12, RED, True), ]) # ═══════════════════════════════════════════════════════════ # SLIDE 12 — Closing # ═══════════════════════════════════════════════════════════ slide = prs.slides.add_slide(prs.slide_layouts[6]) set_slide_bg(slide, NAVY) add_shape(slide, Inches(5.5), Inches(3.3), Inches(2.5), Pt(3), fill_color=GOLD, shape_type=MSO_SHAPE.RECTANGLE) add_text_box(slide, Inches(1), Inches(2), Inches(11), Inches(1), 'תכנית חשמלית — חוות יאיר', font_size=36, color=WHITE, bold=True, alignment=PP_ALIGN.CENTER) add_text_box(slide, Inches(1), Inches(2.8), Inches(11), Inches(0.5), 'מערכת סולארית היברידית | 11.16kWp | 18.8kWh | Zero Export', font_size=20, color=RGBColor(0xA8, 0xD8, 0xEA), alignment=PP_ALIGN.CENTER) add_multiline_box(slide, Inches(1), Inches(3.8), Inches(11), Inches(2), [ ('טופולוגיה סדרתית | ללא CT | DC switch מובנה | C40', 14, GOLD, True, PP_ALIGN.CENTER), ('', 10), ('מסמך זה הוכן לצרכי תכנון בלבד', 14, RGBColor(0xCD, 0xDE, 0xEE), False, PP_ALIGN.CENTER), ('ואינו מהווה תכנית חשמלית רשמית.', 14, RGBColor(0xCD, 0xDE, 0xEE), False, PP_ALIGN.CENTER), ('יש לוודא עם חשמלאי מוסמך לפני ביצוע.', 14, RGBColor(0xCD, 0xDE, 0xEE), True, PP_ALIGN.CENTER), ('', 10), ('אלי ספרא | מרץ 2026', 16, GOLD, False, PP_ALIGN.CENTER), ]) # ── Save ── output_path = os.path.expanduser('~/Documents/GitHub/Baseline/תכנית_חשמלית_חוות_יאיר.pptx') prs.save(output_path) print(f'Saved: {output_path}') print(f'Slides: {len(prs.slides)}')