[ 'read' ], 'public' => [] ]; public static $cache = [ 'read' => 900 // 15 Minutes ]; /** * The user_id column is not present on this table to reduce data overhead. * No public reads are performed on this table so as long as thermostat_id * is always used on reads in this class then this is fine. */ public static $user_locked = false; /** * Read data from the runtime_thermostat table. Basically just a crud read * but has custom security for thermostat_id since user_id is not on the * table. * * @param array $attributes Timestamps can be specified in any format. If no * time zone information is sent, UTC is assumed. * @param array $columns * * @return array */ public function read($attributes = [], $columns = []) { $this->user_lock($attributes['thermostat_id']); // Check for exceptions. if (isset($attributes['thermostat_id']) === false) { throw new \Exception('Missing required attribute: thermostat_id.', 10201); } if (isset($attributes['timestamp']) === false) { throw new \Exception('Missing required attribute: timestamp.', 10202); } if ( is_array($attributes['timestamp']) === true && in_array($attributes['timestamp']['operator'], ['>', '>=', '<', '<=']) === true && is_array($attributes['timestamp']['value']) === true ) { if(count($attributes['timestamp']['value']) === 1) { $attributes['timestamp']['value'] = $attributes['timestamp']['value'][0]; } else { throw new \Exception('Must only specify one timestamp value unless using the "between" operator.', 10204); } } $thermostat = $this->api('thermostat', 'get', $attributes['thermostat_id']); $max_range = 2678000; // 31 days if ( ( is_array($attributes['timestamp']) === true && $attributes['timestamp']['operator'] === 'between' && abs(strtotime($attributes['timestamp']['value'][0]) - strtotime($attributes['timestamp']['value'][1])) > $max_range ) || ( is_array($attributes['timestamp']) === true && in_array($attributes['timestamp']['operator'], ['>', '>=']) === true && time() - strtotime($attributes['timestamp']['value']) > $max_range ) || ( is_array($attributes['timestamp']) === true && in_array($attributes['timestamp']['operator'], ['<', '<=']) === true && strtotime($attributes['timestamp']['value']) - min(strtotime($thermostat['first_connected']), strtotime($thermostat['sync_begin'])) > $max_range ) ) { throw new \Exception('Max range is 31 days.', 10205); } // Accept timestamps in roughly any format; always convert back to something nice and in UTC if (is_array($attributes['timestamp']['value']) === true) { $attributes['timestamp']['value'][0] = date('c', strtotime($attributes['timestamp']['value'][0])); $attributes['timestamp']['value'][1] = date('c', strtotime($attributes['timestamp']['value'][1])); } else { $attributes['timestamp']['value'] = date('c', strtotime($attributes['timestamp']['value'])); } // Read the data. $runtime_thermostats = $this->database->read( 'runtime_thermostat', [ 'timestamp' => $attributes['timestamp'], 'thermostat_id' => $attributes['thermostat_id'] ], [], 'timestamp' // order by ); // Get the appropriate runtime_thermostat_texts. $runtime_thermostat_text_ids = array_unique(array_merge( array_column($runtime_thermostats, 'event_runtime_thermostat_text_id'), array_column($runtime_thermostats, 'climate_runtime_thermostat_text_id') )); $runtime_thermostat_texts = $this->api( 'runtime_thermostat_text', 'read_id', [ 'attributes' => [ 'runtime_thermostat_text_id' => $runtime_thermostat_text_ids ] ] ); // Clean up the data just a bit. foreach ($runtime_thermostats as &$runtime_thermostat) { $runtime_thermostat['timestamp'] = date( 'c', strtotime($runtime_thermostat['timestamp']) ); foreach([ 'indoor_temperature', 'outdoor_temperature', 'setpoint_cool', 'setpoint_heat' ] as $key) { if ($runtime_thermostat[$key] !== null) { $runtime_thermostat[$key] /= 10; } } if ($runtime_thermostat['event_runtime_thermostat_text_id'] !== null) { $runtime_thermostat['event'] = $runtime_thermostat_texts[ $runtime_thermostat['event_runtime_thermostat_text_id'] ]['value']; } else { $runtime_thermostat['event'] = null; } unset($runtime_thermostat['event_runtime_thermostat_text_id']); if ($runtime_thermostat['climate_runtime_thermostat_text_id'] !== null) { $runtime_thermostat['climate'] = $runtime_thermostat_texts[ $runtime_thermostat['climate_runtime_thermostat_text_id'] ]['value']; } else { $runtime_thermostat['climate'] = null; } unset($runtime_thermostat['climate_runtime_thermostat_text_id']); } return $runtime_thermostats; } /** * Since this table does not have a user_id column, security must be handled * manually. Call this with a thermostat_id to verify that the current user * has access to the requested thermostat. * * @param int $thermostat_id * * @throws \Exception If the current user doesn't have access to the * requested thermostat. */ private function user_lock($thermostat_id) { $thermostats = $this->api('thermostat', 'read_id'); if (isset($thermostats[$thermostat_id]) === false) { throw new \Exception('Invalid thermostat_id.', 10203); } } }