# coding=utf-8
"""Window Construction with shades/blinds or a dynamically-controlled glass pane."""
from __future__ import division
from .window import WindowConstruction
from ..material.dictutil import dict_to_material
from ..material.glazing import EnergyWindowMaterialGlazing, \
EnergyWindowMaterialSimpleGlazSys
from ..material.shade import _EnergyWindowMaterialShadeBase, EnergyWindowMaterialBlind
from ..schedule.dictutil import dict_to_schedule
from ..schedule.ruleset import ScheduleRuleset
from ..schedule.fixedinterval import ScheduleFixedInterval
from ..writer import generate_idf_string
from ..properties.extension import WindowConstructionShadeProperties
from honeybee._lockable import lockable
from honeybee.typing import valid_ep_string
[docs]
@lockable
class WindowConstructionShade(object):
"""Window Construction with shades/blinds or a dynamically-controlled glass pane.
Args:
identifier: Text string for a unique Construction ID. Must be < 100 characters
and not contain any EnergyPlus special characters. This will be used to
identify the object across a model and in the exported IDF.
window_construction: A WindowConstruction object that serves as the
"switched off" version of the construction (aka. the "bare construction").
The shade_material and shade_location will be used to modify this
starting construction.
shade_material: An EnergyWindowMaterialShade or an EnergyWindowMaterialBlind
that serves as the shading layer for this construction. This can also
be an EnergyWindowMaterialGlazing, which will indicate that the
WindowConstruction has a dynamically-controlled glass pane like an
electrochromic window assembly.
shade_location: Text to indicate where in the window assembly the shade_material
is located. (Default: "Interior"). Choose from the following 3 options:
* Interior
* Between
* Exterior
Note that the WindowConstruction must have at least one gas gap to use
the "Between" option. Also note that, for a WindowConstruction with more
than one gas gap, the "Between" option defaults to using the inner gap
as this is the only option that EnergyPlus supports.
control_type: Text to indicate how the shading device is controlled, which
determines when the shading is “on” or “off.” (Default: "AlwaysOn").
Choose from the options below (units for the values of the corresponding
setpoint are noted in parentheses next to each option):
* AlwaysOn
* OnIfHighSolarOnWindow (W/m2)
* OnIfHighHorizontalSolar (W/m2)
* OnIfHighOutdoorAirTemperature (C)
* OnIfHighZoneAirTemperature (C)
* OnIfHighZoneCooling (W)
* OnNightIfLowOutdoorTempAndOffDay (C)
* OnNightIfLowInsideTempAndOffDay (C)
* OnNightIfHeatingAndOffDay (W)
setpoint: A number that corresponds to the specified control_type. This can
be a value in (W/m2), (C) or (W) depending upon the control type.
schedule: An optional ScheduleRuleset or ScheduleFixedInterval to be applied
on top of the control_type. If None, the control_type will govern all
behavior of the construction. (Default: None).
Properties:
* identifier
* display_name
* window_construction
* shade_material
* shade_location
* control_type
* setpoint
* schedule
* materials
* layers
* unique_materials
* frame
* r_value
* u_value
* u_factor
* r_factor
* is_symmetric
* is_switchable_glazing
* has_frame
* has_shade
* is_dynamic
* inside_emissivity
* outside_emissivity
* solar_transmittance
* visible_transmittance
* shgc
* thickness
* glazing_count
* gap_count
* is_groupable
* is_zone_groupable
* inside_material
* outside_material
* user_data
* properties
"""
__slots__ = ('_identifier', '_display_name', '_window_construction',
'_shade_material', '_shade_location', '_control_type',
'_setpoint', '_schedule', '_between_gap', '_locked', '_user_data',
"_properties", )
SHADE_LOCATIONS = ('Interior', 'Between', 'Exterior')
CONTROL_TYPES = (
'AlwaysOn', 'OnIfHighSolarOnWindow', 'OnIfHighHorizontalSolar',
'OnIfHighOutdoorAirTemperature', 'OnIfHighZoneAirTemperature',
'OnIfHighZoneCooling', 'OnNightIfLowOutdoorTempAndOffDay',
'OnNightIfLowInsideTempAndOffDay', 'OnNightIfHeatingAndOffDay')
GROUPABLE_TYPES = (
'AlwaysOn', 'OnIfHighHorizontalSolar', 'OnIfHighOutdoorAirTemperature',
'OnNightIfLowOutdoorTempAndOffDay')
ROOM_GROUPABLE_TYPES = (
'OnIfHighZoneAirTemperature', 'OnIfHighZoneCooling',
'OnNightIfLowInsideTempAndOffDay', 'OnNightIfHeatingAndOffDay')
def __init__(self, identifier, window_construction, shade_material,
shade_location='Interior', control_type='AlwaysOn',
setpoint=None, schedule=None):
"""Initialize shaded window construction."""
self._locked = False # unlocked by default
self.identifier = identifier
self._display_name = None
self._between_gap = None # will be used if 'Between' option is used
self._user_data = None
# check that the window construction, shade, and shade location are compatible
assert isinstance(window_construction, WindowConstruction), \
'Expected WindowConstruction for WindowConstructionShade. ' \
'Got {}.'.format(type(window_construction))
shade_types = (_EnergyWindowMaterialShadeBase, EnergyWindowMaterialGlazing)
assert isinstance(shade_material, shade_types), \
'Expected Shade/Blind or Glazing material for WindowConstructionShade. ' \
'Got {}.'.format(type(shade_material))
assert shade_location in self.SHADE_LOCATIONS, \
'Invalid input "{}" for shade location. Must be one ' \
'of the following:\n{}'.format(shade_location, self.SHADE_LOCATIONS)
if isinstance(shade_material, EnergyWindowMaterialGlazing):
ext_pane = window_construction[0]
assert not isinstance(ext_pane, EnergyWindowMaterialSimpleGlazSys), \
'WindowConstruction cannot be a SimpleGlazSys when shading material ' \
'is a glass pane.'
elif shade_location == 'Between': # it's a shade/blind between glass panes
assert window_construction.gap_count >= 1, 'WindowConstruction must have ' \
'at least one gap in order to use "Between" shade_location.'
# calculate the thickness of the gaps on either side of the shade
int_gap = window_construction[-2]
if isinstance(shade_material, EnergyWindowMaterialBlind):
assert shade_material.slat_width < int_gap.thickness, \
'Blind slat_width must be less than the width of the gap in which ' \
'it sits. {} > {}.'.format(
shade_material.slat_width, int_gap.thickness)
shd_thick = 0 if isinstance(shade_material, EnergyWindowMaterialBlind) \
else shade_material.thickness
gap_thick = (int_gap.thickness - shd_thick) / 2
assert gap_thick > 0, \
'Shade thickness is greater than the gap in which it sits.'
# create the split gap material to be used on either side of the shade
between_int_gap = int_gap.duplicate()
between_int_gap.identifier = \
'{}_Split{}'.format(int_gap.identifier, round(gap_thick, 3))
between_int_gap.thickness = gap_thick
self._between_gap = between_int_gap
window_construction.lock() # lock to avoid illegal shade/material combinations
self._window_construction = window_construction
self._shade_material = shade_material
self._shade_location = shade_location
# assign the control type, setpoint and schedule
assert control_type in self.CONTROL_TYPES, \
'Invalid input "{}" for shading control type.' \
' Must be one of the following:\n{}'.format(control_type, self.CONTROL_TYPES)
self._control_type = control_type
self.setpoint = setpoint
self.schedule = schedule
self._properties = WindowConstructionShadeProperties(self)
@property
def identifier(self):
"""Get or set the text string for construction identifier."""
return self._identifier
@identifier.setter
def identifier(self, identifier):
self._identifier = valid_ep_string(identifier, 'construction identifier')
@property
def display_name(self):
"""Get or set a string for the object name without any character restrictions.
If not set, this will be equal to the identifier.
"""
if self._display_name is None:
return self._identifier
return self._display_name
@display_name.setter
def display_name(self, value):
if value is not None:
try:
value = str(value)
except UnicodeEncodeError: # Python 2 machine lacking the character set
pass # keep it as unicode
self._display_name = value
@property
def window_construction(self):
"""Get the WindowConstruction serving as the "switched off" version."""
return self._window_construction
@property
def shade_material(self):
"""Get the material that serves as the shading layer for this construction."""
return self._shade_material
@property
def shade_location(self):
"""Get text to indicate where in the construction the shade_material is located.
This will be either "Interior", "Between" or "Exterior". Note that, for a
WindowConstruction with more than one gas gap, the "Between" option defaults
to using the inner gap as this is the only option that EnergyPlus supports.
"""
return self._shade_location
@property
def control_type(self):
"""Get or set the text indicating how the shading device is controlled.
Choose from the options below:
* AlwaysOn
* OnIfHighSolarOnWindow
* OnIfHighHorizontalSolar
* OnIfHighOutdoorAirTemperature
* OnIfHighZoneAirTemperature
* OnIfHighZoneCooling
* OnNightIfLowOutdoorTempAndOffDay
* OnNightIfLowInsideTempAndOffDay
* OnNightIfHeatingAndOffDay
"""
return self._control_type
@control_type.setter
def control_type(self, value):
assert value in self.CONTROL_TYPES, \
'Invalid input "{}" for shading control type.' \
' Must be one of the following:\n{}'.format(value, self.CONTROL_TYPES)
if value != 'AlwaysOn':
assert self._setpoint is not None, 'Control setpoint must not ' \
'be None to use "{}" control type.'.format(value)
self._control_type = value
@property
def setpoint(self):
"""A number for the setpoint that corresponds to the specified control_type.
This can be a value in (W/m2), (C) or (W) depending upon the control type.
"""
return self._setpoint
@setpoint.setter
def setpoint(self, value):
if value is not None:
try:
value = float(value)
except (ValueError, TypeError):
raise TypeError('Input setpoint must be a number. Got '
'{}: {}.'.format(type(value), value))
else:
assert self._control_type == 'AlwaysOn', 'Control setpoint cannot ' \
'be None for control type "{}"'.format(self._control_type)
self._setpoint = value
@property
def schedule(self):
"""Get or set a fractional schedule to be applied on top of the control_type.
If None, the control_type will govern all behavior of the construction.
"""
return self._schedule
@schedule.setter
def schedule(self, value):
if value is not None:
assert isinstance(value, (ScheduleRuleset, ScheduleFixedInterval)), \
'Expected schedule for window construction shaded schedule. ' \
'Got {}.'.format(type(value))
if value.schedule_type_limit is not None:
assert value.schedule_type_limit.unit == 'fraction', 'Window ' \
'construction schedule should be fractional. Got a schedule ' \
'of unit_type [{}].'.format(value.schedule_type_limit.unit_type)
value.lock() # lock editing in case schedule has multiple references
self._schedule = value
@property
def materials(self):
"""Get the list of materials in the construction (outside to inside).
This will include the shade material layer in its correct position.
"""
base_mats = list(self._window_construction.materials)
if self.is_switchable_glazing:
if self._shade_location == 'Interior':
base_mats[-1] = self._shade_material
elif self._shade_location == 'Exterior' or \
self._window_construction.gap_count == 0:
base_mats[0] = self._shade_material
else: # middle glass pane
base_mats[-3] = self._shade_material
else:
if self._shade_location == 'Interior':
base_mats.append(self._shade_material)
elif self._shade_location == 'Exterior':
base_mats.insert(0, self._shade_material)
else: # between glass shade/blind
base_mats[-2] = self._between_gap
base_mats.insert(-1, self._shade_material)
base_mats.insert(-1, self._between_gap)
return base_mats
@property
def layers(self):
"""Get a list of material identifiers in the construction (outside to inside).
This will include the shade material layer in its correct position.
"""
return [mat.identifier for mat in self.materials]
@property
def unique_materials(self):
"""Get a list of only unique material objects in the construction.
This will include the shade material layer. It will include both types of glass
layers if the construction is a switchable glazing.
"""
if self.is_switchable_glazing:
return list(set(
self._window_construction.materials + (self.shade_material,)))
return list(set(self.materials))
@property
def frame(self):
"""Get a window frame for the frame material surrounding the construction."""
return self._window_construction.frame
@property
def r_value(self):
"""R-value of the bare window construction [m2-K/W] (excluding air films).
Note that this excludes all effects of the shade layer.
"""
return self._window_construction.r_value
@property
def u_value(self):
"""U-value of the bare window construction [W/m2-K] (excluding air films).
Note that this excludes all effects of the shade layer.
"""
return self._window_construction.u_value
@property
def r_factor(self):
"""Bare window construction R-factor [m2-K/W] (with standard air resistances).
Note that this excludes all effects of the shade layer.
Formulas for film coefficients come from EN673 / ISO10292.
"""
return self._window_construction.r_factor
@property
def u_factor(self):
"""Bare window construction U-factor [W/m2-K] (with standard air resistances).
Note that this excludes all effects of the shade layer.
Formulas for film coefficients come from EN673 / ISO10292.
"""
return self._window_construction.u_factor
@property
def solar_transmittance(self):
"""The solar transmittance of the bare window construction at normal incidence.
Note that this excludes all effects of the shade layer.
"""
return self._window_construction.solar_transmittance
@property
def visible_transmittance(self):
"""The visible transmittance of the bare window construction at normal incidence.
Note that this excludes all effects of the shade layer.
"""
return self._window_construction.visible_transmittance
@property
def shgc(self):
"""The solar heat gain coefficient (SHGC) of the bare window construction.
Note that this excludes all effects of the shade layer.
"""
return self._window_construction.shgc
@property
def is_symmetric(self):
"""Get a boolean for whether the construction layers are symmetric.
Symmetric means that the materials in reversed order are equal to those
in the current order (eg. 'Glass', 'Air Gap', 'Glass'). This is particularly
helpful for interior constructions, which need to have matching materials
in reversed order between adjacent Faces.
"""
mats = self.materials
half_mat = int(len(mats) / 2)
for i in range(half_mat):
if mats[i] != mats[-(i + 1)]:
return False
return True
@property
def has_frame(self):
"""Get a boolean noting whether the construction has a frame assigned to it."""
return self._window_construction.has_frame
@property
def has_shade(self):
"""Get a boolean noting whether dynamic materials are in the construction.
This should always be True for this class.
"""
return True
@property
def is_dynamic(self):
"""Get a boolean noting whether the construction is dynamic.
This will always be True for this class.
"""
return True
@property
def is_switchable_glazing(self):
"""Get a boolean to note whether the construction is switchable glazing.
The construction is a switchable glazing if the shade material is a
glass material.
"""
return isinstance(self.shade_material, EnergyWindowMaterialGlazing)
@property
def switched_glass_material(self):
"""Get material replaced by shade glass when construction is switchable glazing.
This can be used to compare the properties of the glass layer replaced by
the shade glass. Will be None if the construction is not a switchable glazing.
"""
if not self.is_switchable_glazing:
return None
base_mats = self._window_construction.materials
if self._shade_location == 'Interior':
return self.base_mats[-1]
elif self._shade_location == 'Exterior' or \
self._window_construction.gap_count == 0:
return base_mats[0]
else: # middle glass pane
return base_mats[-3]
@property
def inside_emissivity(self):
""""The emissivity of the inside face of the construction.
This will use the emissivity of the shade layer if it is interior.
"""
mats = self.materials
if isinstance(mats[-1], EnergyWindowMaterialSimpleGlazSys):
return 0.84
try:
return mats[-1].emissivity_back
except AttributeError:
return mats[-1].emissivity
@property
def outside_emissivity(self):
""""The emissivity of the outside face of the construction.
This will use the emissivity of the shade layer if it is interior.
"""
mats = self.materials
if isinstance(mats[0], EnergyWindowMaterialSimpleGlazSys):
return 0.84
return mats[0].emissivity
@property
def thickness(self):
"""Thickness of the construction [m], excluding the shade layer.
This is effectively the thickness that EnergyPlus assumes.
"""
return self._window_construction.thickness
@property
def glazing_count(self):
"""The number of glazing materials contained within the construction.
Note that Simple Glazing System materials do not count.
"""
return self._window_construction.glazing_count
@property
def gap_count(self):
"""Get the number of gas gaps contained within the construction."""
count = self._window_construction.gap_count
if self.shade_location == 'Between' and not self.is_switchable_glazing:
count += 1
return count
@property
def is_groupable(self):
"""Get a boolean for whether controls allow the construction to be grouped."""
return self.control_type in self.GROUPABLE_TYPES
@property
def is_room_groupable(self):
"""Get a boolean for whether controls allow grouping by room."""
return self.control_type in self.ROOM_GROUPABLE_TYPES
@property
def inside_material(self):
"""The the inside material layer of the construction.
Useful for checking that an asymmetric construction is correctly assigned.
"""
return self.materials[-1]
@property
def outside_material(self):
"""The the outside material layer of the construction.
Useful for checking that an asymmetric construction is correctly assigned.
"""
return self.materials[0]
@property
def _ep_shading_type(self):
"""Text for the Shading Type field that EnergyPlus wants in the IDF."""
if self.is_switchable_glazing:
return 'SwitchableGlazing'
elif isinstance(self.shade_material, EnergyWindowMaterialBlind):
return 'BetweenGlassBlind' if self.shade_location == 'Between' \
else '{}Blind'.format(self.shade_location)
else:
return 'BetweenGlassShade' if self.shade_location == 'Between' \
else '{}Shade'.format(self.shade_location)
@property
def user_data(self):
"""Get or set an optional dictionary for additional meta data for this object.
This will be None until it has been set. All keys and values of this
dictionary should be of a standard Python type to ensure correct
serialization of the object to/from JSON (eg. str, float, int, list, dict)
"""
if self._user_data is not None:
return self._user_data
@user_data.setter
def user_data(self, value):
if value is not None:
assert isinstance(value, dict), 'Expected dictionary for honeybee_energy' \
'object user_data. Got {}.'.format(type(value))
self._user_data = value
@property
def properties(self):
"""Get properties for extensions."""
return self._properties
[docs]
@classmethod
def from_dict(cls, data):
"""Create a WindowConstructionShade from a dictionary.
Note that the dictionary must be a non-abridged version for this
classmethod to work.
Args:
data: A python dictionary in the following format
.. code-block:: python
{
"type": 'WindowConstructionShade',
"identifier": 'Double Pane U-250 IntBlind-0025',
"display_name": 'Double Pane with Interior Blind',
"window_construction": {} # a WindowConstruction dictionary representation
"shade_material": {} # a shade/blind/glass dictionary representation
"shade_location": 'Interior', # text for shade layer location
"control_type": 'OnIfHighSolarOnWindow', # text for shade control type
"setpoint": 200, # number for control setpoint
"schedule": {} # optional ScheduleRuleset or ScheduleFixedInterval dict
}
"""
# check the type
assert data['type'] == 'WindowConstructionShade', \
'Expected WindowConstructionShade. Got {}.'.format(data['type'])
# re-serialize required inputs
window_constr = WindowConstruction.from_dict(data['window_construction'])
shade_material = dict_to_material(data['shade_material'])
# re-serialize optional inputs
shade_location, control_type, setpoint = cls._from_dict_defaults(data)
schedule = dict_to_schedule(data['schedule']) if 'schedule' in data and \
data['schedule'] is not None else None
new_obj = cls(data['identifier'], window_constr, shade_material, shade_location,
control_type, setpoint, schedule)
if 'display_name' in data and data['display_name'] is not None:
new_obj.display_name = data['display_name']
if 'user_data' in data and data['user_data'] is not None:
new_obj.user_data = data['user_data']
if 'properties' in data and data['properties'] is not None:
new_obj.properties._load_extension_attr_from_dict(data['properties'])
return new_obj
[docs]
@classmethod
def from_dict_abridged(cls, data, materials, schedules):
"""Create a WindowConstructionShade from an abridged dictionary.
Args:
data: An WindowConstructionShade dictionary with the format below.
materials: A dictionary with identifiers of materials as keys and
Python material objects as values.
schedules: A dictionary with schedule identifiers as keys and
honeybee schedule objects as values.
.. code-block:: python
{
"type": 'WindowConstructionShadeAbridged',
"identifier": 'Double Pane U-250 IntBlind-0025',
"display_name": 'Double Pane with Interior Blind',
"window_construction": {} # a WindowConstructionAbridged dictionary
"shade_material": 'Blind-0025' # a shade/blind/glass identifier
"shade_location": 'Interior', # text for shade layer location
"control_type": 'OnIfHighSolarOnWindow', # text for shade control type
"setpoint": 200, # number for control setpoint
"schedule": 'DayNight_Schedule' # optional schedule identifier
}
"""
# check the type
assert data['type'] == 'WindowConstructionShadeAbridged', \
'Expected WindowConstructionShadeAbridged. Got {}.'.format(data['type'])
# re-serialize required inputs
window_constr = WindowConstruction.from_dict_abridged(
data['window_construction'], materials)
shade_material = materials[data['shade_material']]
# re-serialize optional inputs
shade_location, control_type, setpoint = cls._from_dict_defaults(data)
schedule = schedules[data['schedule']] if 'schedule' in data and \
data['schedule'] is not None else None
new_obj = cls(data['identifier'], window_constr, shade_material, shade_location,
control_type, setpoint, schedule)
if 'display_name' in data and data['display_name'] is not None:
new_obj.display_name = data['display_name']
if 'user_data' in data and data['user_data'] is not None:
new_obj.user_data = data['user_data']
if 'properties' in data and data['properties'] is not None:
new_obj.properties._load_extension_attr_from_dict(data['properties'])
return new_obj
[docs]
def to_idf(self):
"""IDF string representation of construction object.
Note that this method only outputs a single string for the bare window
construction and, to write the full construction into an IDF, the
construction's unique_materials must also be written along with the
output of to_shaded_idf, which contains the shaded construction.
Also, for each Aperture to which this construction is assigned, a
ShadingControl object must also be written, which can be obtained from
the to_shading_control_idf. If the construction has a frame, the frame
definition must also be written.
Returns:
Text string representation of the bare (unshaded) construction.
.. code-block:: shell
Construction,
Generic Double Pane, !- name
Generic Low-e Glass, !- layer 1
Generic Window Air Gap, !- layer 2
Generic Clear Glass; !- layer 3
"""
return self._window_construction.to_idf()
[docs]
def to_shaded_idf(self):
"""IDF string representation of construction in its shaded state.
Returns:
Text string representation of the shaded construction.
"""
materials = self.materials
values = (self.identifier,) + tuple(mat.identifier for mat in materials)
comments = ('name',) + tuple('layer %s' % (i + 1) for i in range(len(materials)))
return generate_idf_string('Construction', values, comments)
[docs]
def to_shading_control_idf(self, aperture_identifier, room_identifier):
"""IDF string representation of a WindowShadingControl object.
This has to be written for every Aperture to which this construction is
assigned in order for EnergyPlus to simulate it correctly.
Args:
aperture_identifier: The identifier of the honeybee Aperture to
which this construction is assigned.
room_identifier: The identifier of the honeybee Room to which the
aperture belongs.
Returns:
Text string representation of the WindowShadingControl.
"""
control_name = '{}_ShdControl'.format(aperture_identifier)
control_type = 'OnIfScheduleAllows' if self.schedule is not None and \
self.control_type == 'AlwaysOn' else self.control_type
sch = self.schedule.identifier if self.schedule is not None else ''
sch_bool = 'Yes' if self.schedule is not None else 'No'
setpt = self.setpoint if self.setpoint is not None else ''
values = (control_name, room_identifier, 1, self._ep_shading_type,
self.identifier, control_type, sch, setpt, sch_bool,
'', '', '', '', '', '', '', aperture_identifier)
comments = \
('name', 'zone name', 'sequence number', 'shading type',
'construction with shade', 'control type', 'schedule', 'setpoint',
'is scheduled', 'is glare controlled', 'shade material', 'slat control',
'slat schedule', 'setpoint 2', 'daylight object', 'multiple control type',
'fenestration surface')
return generate_idf_string('WindowShadingControl', values, comments)
[docs]
def to_radiance_solar(self):
"""Honeybee Radiance material for the bare (unshaded) construction."""
# TODO: add method that represents blinds with BSDF + shades with Trans
return self._window_construction.to_radiance_solar()
[docs]
def to_radiance_visible(self):
"""Honeybee Radiance material for the bare (unshaded) construction."""
# TODO: add method that represents blinds with BSDF + shades with Trans
return self._window_construction.to_radiance_visible()
[docs]
def to_dict(self, abridged=False):
"""Window construction dictionary representation.
Args:
abridged: Boolean to note whether the full dictionary describing the
object should be returned (False) or just an abridged version (True),
which only specifies the identifiers of material layers and
schedules. (Default: False).
"""
base = {'type': 'WindowConstructionShade'} if not \
abridged else {'type': 'WindowConstructionShadeAbridged'}
base['identifier'] = self.identifier
base['window_construction'] = self.window_construction.to_dict(abridged)
base['shade_material'] = self.shade_material.identifier if abridged \
else self.shade_material.to_dict()
base['shade_location'] = self.shade_location
base['control_type'] = self.control_type
if self.control_type != 'AlwaysOn':
base['setpoint'] = self.setpoint
if self.schedule is not None:
base['schedule'] = self.schedule.identifier if abridged \
else self.schedule.to_dict()
if self._display_name is not None:
base['display_name'] = self.display_name
if self._user_data is not None:
base['user_data'] = self.user_data
base['properties'] = self.properties.to_dict() if self._properties else None
return base
[docs]
def lock(self):
"""The lock() method will also lock the shade_material."""
self._locked = True
self.shade_material.lock()
[docs]
def unlock(self):
"""The unlock() method will also unlock the shade_material."""
self._locked = False
self.shade_material.unlock()
[docs]
def duplicate(self):
"""Get a copy of this construction."""
return self.__copy__()
@staticmethod
def _from_dict_defaults(data):
"Re-serialize default values from a dictionary."
shade_location = data['shade_location'] if 'shade_location' in data and \
data['shade_location'] is not None else 'Interior'
control_type = data['control_type'] if 'control_type' in data and \
data['control_type'] is not None else 'AlwaysOn'
setpoint = data['setpoint'] if 'setpoint' in data \
else None
return shade_location, control_type, setpoint
def __copy__(self):
new_con = WindowConstructionShade(
self.identifier, self.window_construction, self.shade_material,
self.shade_location, self.control_type, self.setpoint,
self.schedule)
new_con._between_gap = self._between_gap
new_con._display_name = self._display_name
new_con._user_data = None if self._user_data is None else self._user_data.copy()
new_con._properties._duplicate_extension_attr(self._properties)
return new_con
def __key(self):
"""A tuple based on the object properties, useful for hashing."""
sch = hash(self.schedule) if self.schedule is not None else None
return (self._identifier, hash(self.window_construction),
hash(self.shade_material), self.shade_location, self.control_type,
self.setpoint, sch)
def __hash__(self):
return hash(self.__key())
def __eq__(self, other):
return isinstance(other, WindowConstructionShade) and \
self.__key() == other.__key()
def __ne__(self, other):
return not self.__eq__(other)
[docs]
def ToString(self):
"""Overwrite .NET ToString."""
return self.__repr__()
def __repr__(self):
return self.to_shaded_idf()