import typing
from marshmallow import Schema, ValidationError, fields, post_load, validate
import caspia.node
from caspia.node import components
[docs]class NodeRefField(fields.String):
def _validate(self, value):
super()._validate(value)
if value not in self.context.get('nodes', []):
raise ValidationError(f'undefined node {value}')
[docs]class PinField(fields.Integer):
def _deserialize(self, value, attr, data):
int_val = super()._deserialize(value, attr, data)
return caspia.node.Pin(int_val)
[docs]class PinModeField(fields.String):
def _deserialize(self, value, attr, data):
str_val = super()._deserialize(value, attr, data)
if str_val == 'up':
return components.DigitalInput.Config.MODE_PULL_UP
elif str_val == 'down':
return components.DigitalInput.Config.MODE_PULL_DOWN
else:
raise ValidationError('invalid pin mode', data=value)
[docs]class TSL258XSensorGainControlField(fields.Integer):
def _deserialize(self, value, attr, data):
TSL258XSensor = components.sensors.tsl258x.TSL258XSensor
int_val = super()._deserialize(value, attr, data)
if int_val == 1:
return TSL258XSensor.GainControl.Gain1x
elif int_val == 8:
return TSL258XSensor.GainControl.Gain8x
elif int_val == 16:
return TSL258XSensor.GainControl.Gain16x
elif int_val == 111:
return TSL258XSensor.GainControl.Gain111x
else:
msg = 'invalid gain, valid values are 1, 8, 16 and 111'
raise ValidationError(msg, data=data)
[docs]class ComponentSchema(Schema):
config_cls: typing.Callable
node = NodeRefField(required=True)
[docs] def make_config(self, data):
self.context['node'] = data.pop('node', None)
return self.config_cls(**data)
@post_load
def _make_config(self, data):
return self.make_config(data)
[docs]class RelaySchema(ComponentSchema):
config_cls = components.Relay.Config
pin = PinField(required=True)
inverted = fields.Boolean(missing=False)
initial_state = fields.Boolean(missing=False)
timeout = fields.Number(missing=0.0, validate=lambda x: x >= 0.0)
[docs]class SensorSchema(ComponentSchema):
interval = fields.Integer(missing=0, validate=lambda x: 0 <= x < 0xFFFF)
[docs]class MCP980XSensorSchema(SensorSchema):
config_cls = components.sensors.MCP980XSensor.Config
address = fields.Integer()
resolution = fields.Integer(
validate=lambda x: 0 <= x < components.sensors.MCP980XSensor.Config.RESOLUTION_12b)
[docs]class AnalogSensorSchema(SensorSchema):
config_cls = components.sensors.AnalogSensor.Config
pin = PinField(required=True)
power_pin = PinField()
samples = fields.Integer(missing=1, validate=lambda x: 1 <= x < 0xFF)
[docs]class BlindsSchema(ComponentSchema):
[docs] class TimingSchema(Schema):
move_up_delay = fields.Number(required=True)
move_down_delay = fields.Number(required=True)
direction_change_delay = fields.Number(required=True)
tilt_time = fields.Number(required=True)
move_up_time = fields.Number(required=True)
move_down_time = fields.Number(required=True)
arrest_time = fields.Number(required=True)
backlash = fields.Number()
config_cls = components.Blinds.Config
power_pin = PinField(required=True, attribute='pwr_pin')
direction_pin = PinField(required=True, attribute='dir_pin')
direction_inverted = fields.Boolean(missing=False, attribute='dir_pin_inverted')
calibrate_at_bottom = fields.Boolean(missing=False)
timing = fields.Nested(TimingSchema, required=True)
[docs] def make_config(self, data):
timing = data.pop('timing')
data.update(timing)
return super().make_config(data)
[docs]class S300Schema(SensorSchema):
config_cls = components.sensors.S300Sensor.Config
[docs]class SerialSchema(ComponentSchema):
config_cls = components.Serial.Config
baudrate = fields.Integer(required=True)
[docs]class SHT2XSensorSchema(SensorSchema):
config_cls = components.sensors.SHT2XSensor.Config
[docs]class APDS9300SensorSchema(SensorSchema):
config_cls = components.sensors.APDS9300Sensor.Config
address = fields.Integer()
[docs]class SCD30SensorSchema(SensorSchema):
config_cls = components.sensors.SCD30Sensor.Config
temperature_offset = fields.Integer(validate=lambda x: 0 <= x <= 0xFFFF)
ambient_pressure = fields.Integer(validate=lambda x: x == 0 or 700 <= x <= 1200)
[docs]class TSL258XSensorSchema(SensorSchema):
config_cls = components.sensors.TSL258XSensor.Config
gain_control = TSL258XSensorGainControlField(missing=1, load_from='gain')
integration_cycles = fields.Integer(missing=1, validate=validate.Range(min=1, max=255))