GladysProject/Gladys

View on GitHub
server/models/scene.js

Summary

Maintainability
B
6 hrs
Test Coverage
const Joi = require('@hapi/joi').extend(require('@hapi/joi-date'));
const { ACTION_LIST, EVENT_LIST, ALARM_MODES_LIST } = require('../utils/constants');
const { addSelector } = require('../utils/addSelector');
const iconList = require('../config/icons.json');

const actionSchema = Joi.array().items(
  Joi.array().items(
    Joi.object().keys({
      type: Joi.string()
        .valid(...ACTION_LIST)
        .required(),
      device_feature: Joi.string(),
      device_features: Joi.array().items(Joi.string()),
      device: Joi.string(),
      devices: Joi.array().items(Joi.string()),
      user: Joi.string(),
      house: Joi.string(),
      scene: Joi.string(),
      camera: Joi.string(),
      text: Joi.string(),
      value: Joi.number(),
      evaluate_value: Joi.string(),
      minutes: Joi.number(),
      unit: Joi.string(),
      url: Joi.string().uri(),
      body: Joi.string(),
      method: Joi.string().valid('get', 'post', 'patch', 'put', 'delete'),
      days_of_the_week: Joi.array().items(
        Joi.string().valid('monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'),
      ),
      before: Joi.string().regex(/^([0-9]{2}):([0-9]{2})$/),
      after: Joi.string().regex(/^([0-9]{2}):([0-9]{2})$/),
      calendar_event_name_comparator: Joi.string().valid(
        'is-exactly',
        'contains',
        'starts-with',
        'ends-with',
        'has-any-name',
      ),
      calendars: Joi.array().items(Joi.string()),
      calendar_event_name: Joi.string(),
      stop_scene_if_event_found: Joi.boolean(),
      stop_scene_if_event_not_found: Joi.boolean(),
      request_response_keys: Joi.array().items(Joi.string()),
      ecowatt_network_status: Joi.string().valid('ok', 'warning', 'critical'),
      edf_tempo_peak_day_type: Joi.string().valid('blue', 'white', 'red', 'no-check'),
      edf_tempo_day: Joi.string().valid('today', 'tomorrow'),
      edf_tempo_peak_hour_type: Joi.string().valid('peak-hour', 'off-peak-hour', 'no-check'),
      headers: Joi.array().items(
        Joi.object().keys({
          key: Joi.string(),
          value: Joi.string(),
        }),
      ),
      conditions: Joi.array().items({
        variable: Joi.string().required(),
        operator: Joi.string()
          .valid('=', '!=', '>', '>=', '<', '<=')
          .required(),
        value: Joi.number(),
        evaluate_value: Joi.string(),
      }),
      alarm_mode: Joi.string().valid(...ALARM_MODES_LIST),
      topic: Joi.string(),
      message: Joi.string().allow(''),
      blinking_time: Joi.number(),
      blinking_speed: Joi.string().valid('slow', 'medium', 'fast'),
    }),
  ),
);

const triggersSchema = Joi.array().items(
  Joi.object().keys({
    type: Joi.string()
      .valid(...EVENT_LIST)
      .required(),
    house: Joi.string(),
    device: Joi.string(),
    device_feature: Joi.string(),
    operator: Joi.string().valid('=', '!=', '>', '>=', '<', '<='),
    value: Joi.number(),
    user: Joi.string(),
    area: Joi.string(),
    scheduler_type: Joi.string().valid('every-month', 'every-week', 'every-day', 'interval', 'custom-time'),
    // Calendar event
    calendar_event_attribute: Joi.string().valid('start', 'end'),
    calendar_event_name_comparator: Joi.string().valid(
      'is-exactly',
      'contains',
      'starts-with',
      'ends-with',
      'has-any-name',
    ),
    calendars: Joi.array().items(Joi.string()),
    calendar_event_name: Joi.string(),
    duration: Joi.number(),
    // End of calendar checks
    date: Joi.date().format('YYYY-MM-DD'),
    time: Joi.string().regex(/^([0-9]{2}):([0-9]{2})$/),
    interval: Joi.number(),
    unit: Joi.string(),
    days_of_the_week: Joi.array().items(
      Joi.string().valid('monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'),
    ),
    day_of_the_month: Joi.number()
      .min(1)
      .max(31),
    threshold_only: Joi.boolean(),
    topic: Joi.string(),
    message: Joi.string().allow(''),
  }),
);

module.exports = (sequelize, DataTypes) => {
  const scene = sequelize.define(
    't_scene',
    {
      id: {
        type: DataTypes.UUID,
        primaryKey: true,
        defaultValue: DataTypes.UUIDV4,
      },
      name: {
        allowNull: false,
        type: DataTypes.STRING,
      },
      description: {
        allowNull: true,
        type: DataTypes.STRING,
      },
      icon: {
        allowNull: false,
        type: DataTypes.ENUM(iconList),
      },
      active: {
        allowNull: false,
        type: DataTypes.BOOLEAN,
        defaultValue: true,
      },
      selector: {
        allowNull: false,
        unique: true,
        type: DataTypes.STRING,
        validate: {
          is: /^[a-z0-9]+(?:-[a-z0-9]+)*$/,
        },
      },
      actions: {
        allowNull: false,
        type: DataTypes.JSON,
        validate: {
          isEven(value) {
            const result = actionSchema.validate(value);
            if (result.error) {
              throw new Error(result.error.details[0].message);
            }
          },
        },
      },
      triggers: {
        type: DataTypes.JSON,
        validate: {
          isEven(value) {
            const result = triggersSchema.validate(value);
            if (result.error) {
              throw new Error(result.error.details[0].message);
            }
          },
        },
      },
      last_executed: {
        type: DataTypes.DATE,
      },
    },
    {},
  );

  // add slug if needed
  scene.beforeValidate(addSelector);

  scene.associate = (models) => {
    scene.hasMany(models.TagScene, {
      foreignKey: 'scene_id',
      sourceKey: 'id',
      as: 'tags',
    });
  };

  return scene;
};