api / scripts /create_pptx.py
safraeli's picture
Deploy: 2026 sensor migration + redesign + bucket B endpoints
13fc29d verified
"""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)}')