Add library
This commit is contained in:
commit
836a60480d
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
vendor/
|
58
auth.go
Normal file
58
auth.go
Normal file
@ -0,0 +1,58 @@
|
||||
package tiko
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
baseURL = "https://particuliers-tiko.fr/api/v3"
|
||||
graphqlAPIURL = baseURL + "/graphql/"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID int64 `json:"id"`
|
||||
Properties []Property `json:"properties"`
|
||||
}
|
||||
|
||||
type AuthResp struct {
|
||||
LogIn struct {
|
||||
User User `json:"user"`
|
||||
Token string `json:"token"`
|
||||
} `json:"logIn"`
|
||||
}
|
||||
|
||||
type ApiResp[T any] struct {
|
||||
Data T `json:"data"`
|
||||
}
|
||||
|
||||
func (c *Client) Authenticate(username, password string) (*User, string, error) {
|
||||
requestData := map[string]interface{}{
|
||||
"operationName": "LogIn",
|
||||
"variables": map[string]string{
|
||||
"email": username,
|
||||
"password": password,
|
||||
},
|
||||
"query": "mutation LogIn($email: String!, $password: String!, $langCode: String, $retainSession: Boolean) { logIn( input: { email: $email password: $password langCode: $langCode retainSession: $retainSession}) { user { id properties { id } } token }}",
|
||||
}
|
||||
|
||||
// Convertir les données en JSON
|
||||
requestDataJSON, err := json.Marshal(requestData)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Créer la requête HTTP
|
||||
req, err := http.NewRequest(http.MethodPost, graphqlAPIURL, bytes.NewBuffer(requestDataJSON))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
resp, err := Do[ApiResp[AuthResp]](c, req)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return &resp.Data.LogIn.User, resp.Data.LogIn.Token, nil
|
||||
}
|
122
client.go
Normal file
122
client.go
Normal file
@ -0,0 +1,122 @@
|
||||
package tiko
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/http/httputil"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMissingConfiguration = errors.New("missing configuration")
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Debug bool `json:"debug,omitempty"`
|
||||
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
config *Config
|
||||
httpClient *http.Client
|
||||
|
||||
user *User
|
||||
token string
|
||||
}
|
||||
|
||||
func NewClient(cfg *Config) *Client {
|
||||
jar, _ := cookiejar.New(&cookiejar.Options{})
|
||||
return &Client{
|
||||
config: cfg,
|
||||
httpClient: &http.Client{
|
||||
Jar: jar,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Debug() bool {
|
||||
if c.config != nil {
|
||||
return c.config.Debug
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Client) Init() error {
|
||||
if c.user != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.config == nil {
|
||||
return ErrMissingConfiguration
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
c.user, c.token, err = c.Authenticate(c.config.Username, c.config.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) GetUser() (*User, error) {
|
||||
if c.user != nil {
|
||||
return c.user, nil
|
||||
}
|
||||
|
||||
if err := c.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.user, nil
|
||||
}
|
||||
|
||||
func Do[T any](c *Client, req *http.Request) (*T, error) {
|
||||
// Ajouter les en-têtes à la requête
|
||||
if c.token != "" {
|
||||
req.Header.Set("Authorization", "token "+c.token)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// Effectuer la requête HTTP
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
if c.Debug() {
|
||||
d, _ := httputil.DumpRequest(req, true)
|
||||
fmt.Println("Request\n", string(d))
|
||||
|
||||
d, _ = httputil.DumpResponse(resp, true)
|
||||
fmt.Println("Response\n", string(d))
|
||||
}
|
||||
|
||||
return nil, errors.New("bad http status")
|
||||
}
|
||||
|
||||
// Lire le corps de la réponse
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Décoder la réponse JSON
|
||||
// var historyResponse HistoryResponse
|
||||
var obj T
|
||||
err = json.Unmarshal(body, &obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &obj, nil
|
||||
}
|
56
history.go
Normal file
56
history.go
Normal file
@ -0,0 +1,56 @@
|
||||
package tiko
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HistoryResponse struct {
|
||||
Status string `json:"status"`
|
||||
Response struct {
|
||||
Timestamps []float64 `json:"timestamps"`
|
||||
Values []float64 `json:"values"`
|
||||
Kbox string `json:"kbox"`
|
||||
Version string `json:"version"`
|
||||
MissingData []int `json:"missing_data"`
|
||||
Control []int `json:"control"`
|
||||
EcoMode []int `json:"eco_mode"`
|
||||
Production []int `json:"production"`
|
||||
Resolution string `json:"resolution"`
|
||||
ValuesTargetTemperature []float64 `json:"values_target_temperature"`
|
||||
ValuesMeasuredTemperature []float64 `json:"values_measured_temperature"`
|
||||
Start int64 `json:"start"`
|
||||
End int64 `json:"end"`
|
||||
ValuesList []int `json:"values_list"`
|
||||
} `json:"response"`
|
||||
}
|
||||
|
||||
func (c *Client) GetRoomHistory(propertyID, roomID int64, startDate, endDate time.Time, resolution string) (*HistoryResponse, error) {
|
||||
if err := c.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convertir les dates en millisecondes UNIX
|
||||
start := startDate.UnixNano() / int64(time.Millisecond)
|
||||
end := endDate.UnixNano() / int64(time.Millisecond)
|
||||
|
||||
// Construire l'URL avec les paramètres
|
||||
path := fmt.Sprintf("%s/properties/%d/rooms/%d/history/", baseURL, propertyID, roomID)
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("start", strconv.FormatInt(start, 10))
|
||||
params.Set("end", strconv.FormatInt(end, 10))
|
||||
params.Set("resolution", resolution)
|
||||
url := fmt.Sprintf("%s?%s", path, params.Encode())
|
||||
|
||||
// Créer la requête HTTP
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return Do[HistoryResponse](c, req)
|
||||
}
|
57
property.go
Normal file
57
property.go
Normal file
@ -0,0 +1,57 @@
|
||||
package tiko
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Property struct {
|
||||
ID int64 `json:"id"`
|
||||
Mode struct {
|
||||
Boost bool `json:"boost"`
|
||||
Frost bool `json:"frost"`
|
||||
Absence bool `json:"absence"`
|
||||
DisableHeating bool `json:"disableHeating"`
|
||||
} `json:"mode"`
|
||||
TypeName string `json:"__typename"`
|
||||
Rooms []Room
|
||||
}
|
||||
|
||||
type PropertyResp struct {
|
||||
Property Property `json:"property"`
|
||||
}
|
||||
|
||||
func (c *Client) GetProperty(propertyID int64) (*Property, error) {
|
||||
if err := c.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Créer les données de la requête
|
||||
requestData := map[string]interface{}{
|
||||
"operationName": "GET_PROPERTY_OVERVIEW_DECENTRALISED",
|
||||
"variables": map[string]int64{
|
||||
"id": propertyID,
|
||||
},
|
||||
"query": "query GET_PROPERTY_OVERVIEW_DECENTRALISED($id: Int!, $excludeRooms: [Int]) { property(id: $id) { id mode rooms(excludeRooms: $excludeRooms) { id name type color heaters currentTemperatureDegrees targetTemperatureDegrees humidity sensors mode { boost absence frost disableHeating __typename } status { disconnected heaterDisconnected heatingOperating sensorBatteryLow sensorDisconnected temporaryAdjustment heatersRegulated heaterCalibrationState __typename } __typename } __typename } }",
|
||||
}
|
||||
|
||||
// Convertir les données en JSON
|
||||
requestDataJSON, err := json.Marshal(requestData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Créer la requête HTTP
|
||||
req, err := http.NewRequest(http.MethodPost, graphqlAPIURL, bytes.NewBuffer(requestDataJSON))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := Do[ApiResp[PropertyResp]](c, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &resp.Data.Property, nil
|
||||
}
|
172
room.go
Normal file
172
room.go
Normal file
@ -0,0 +1,172 @@
|
||||
package tiko
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type RoomMode string
|
||||
|
||||
const (
|
||||
RoomModeBoost = RoomMode("boost")
|
||||
RoomModeFalse = RoomMode("false")
|
||||
RoomModeAway = RoomMode("absence")
|
||||
RoomModeFrost = RoomMode("frost")
|
||||
RoomModeDisable = RoomMode("disableHeating")
|
||||
)
|
||||
|
||||
type Room struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type int `json:"type"`
|
||||
Color string `json:"color"`
|
||||
Heaters int `json:"heaters"`
|
||||
CurrentTemperature float64 `json:"currentTemperatureDegrees"`
|
||||
TargetTemperature float64 `json:"targetTemperatureDegrees"`
|
||||
Humidity *int `json:"humidity"`
|
||||
Sensors int `json:"sensors"`
|
||||
Mode struct {
|
||||
Boost bool `json:"boost"`
|
||||
Absence bool `json:"absence"`
|
||||
Frost bool `json:"frost"`
|
||||
DisableHeating bool `json:"disableHeating"`
|
||||
TypeName string `json:"__typename"`
|
||||
} `json:"mode"`
|
||||
Status struct {
|
||||
Disconnected bool `json:"disconnected"`
|
||||
// HeaterDisconnected bool `json:"heaterDisconnected"`
|
||||
HeatingOperating bool `json:"heatingOperating"`
|
||||
SensorBatteryLow bool `json:"sensorBatteryLow"`
|
||||
SensorDisconnected bool `json:"sensorDisconnected"`
|
||||
// TemporaryAdjustment bool `json:"temporaryAdjustment"`
|
||||
HeatersRegulated bool `json:"heatersRegulated"`
|
||||
HeaterCalibrationState [][2]interface{} `json:"heaterCalibrationState"`
|
||||
TypeName string `json:"__typename"`
|
||||
} `json:"status"`
|
||||
TypeName string `json:"__typename"`
|
||||
}
|
||||
|
||||
type PropertyRoom struct {
|
||||
Room Room `json:"room"`
|
||||
}
|
||||
|
||||
type PropertyRoomResp struct {
|
||||
Property PropertyRoom `json:"property"`
|
||||
}
|
||||
|
||||
func (c *Client) GetRoom(propertyID, roomID int64) (*Room, error) {
|
||||
if err := c.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Créer les données de la requête
|
||||
requestData := map[string]interface{}{
|
||||
"operationName": "GET_PROPERTY_MODE_AND_ROOM",
|
||||
"variables": map[string]int64{
|
||||
"propertyId": propertyID,
|
||||
"roomId": roomID,
|
||||
},
|
||||
"query": "query GET_PROPERTY_MODE_AND_ROOM($propertyId: Int!, $roomId: Int!) { property(id: $propertyId) { id mode room(id: $roomId) { id name type color heaters currentTemperatureDegrees targetTemperatureDegrees humidity sensors mode { boost absence frost disableHeating __typename } status { disconnected heaterDisconnected heatingOperating sensorBatteryLow sensorDisconnected temporaryAdjustment heatersRegulated heaterCalibrationState __typename } __typename } __typename } }",
|
||||
}
|
||||
|
||||
// Convertir les données en JSON
|
||||
requestDataJSON, err := json.Marshal(requestData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Créer la requête HTTP
|
||||
req, err := http.NewRequest(http.MethodPost, graphqlAPIURL, bytes.NewBuffer(requestDataJSON))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := Do[ApiResp[PropertyRoomResp]](c, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &resp.Data.Property.Room, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetRoomMode(propertyID, roomID int64, mode RoomMode) (*Room, error) {
|
||||
if err := c.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Créer les données de la requête
|
||||
requestData := map[string]interface{}{
|
||||
"operationName": "SET_ROOM_MODE",
|
||||
"variables": map[string]interface{}{
|
||||
"propertyId": propertyID,
|
||||
"roomId": roomID,
|
||||
"mode": mode,
|
||||
},
|
||||
"query": "mutation SET_ROOM_MODE($propertyId: Int!, $roomId: Int!, $mode: String!) { setRoomMode(input: {propertyId: $propertyId, roomId: $roomId, mode: $mode}) { id mode { boost absence frost disableHeating __typename } __typename } }",
|
||||
}
|
||||
|
||||
// Convertir les données en JSON
|
||||
requestDataJSON, err := json.Marshal(requestData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Créer la requête HTTP
|
||||
req, err := http.NewRequest(http.MethodPost, graphqlAPIURL, bytes.NewBuffer(requestDataJSON))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := Do[map[string]interface{}](c, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println("SetRoomMode Resp", resp)
|
||||
|
||||
return c.GetRoom(propertyID, roomID)
|
||||
}
|
||||
|
||||
func (c *Client) SetRoomTemperature(propertyID, roomID int64, temp float32) (*Room, error) {
|
||||
if err := c.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if temp <= 7 || temp >= 25 {
|
||||
return nil, errors.New("temp is out of bounds")
|
||||
}
|
||||
|
||||
// Créer les données de la requête
|
||||
requestData := map[string]interface{}{
|
||||
"operationName": "SET_PROPERTY_ROOM_ADJUST_TEMPERATURE",
|
||||
"variables": map[string]interface{}{
|
||||
"propertyId": propertyID,
|
||||
"roomId": roomID,
|
||||
"temperature": temp,
|
||||
},
|
||||
"query": "mutation SET_PROPERTY_ROOM_ADJUST_TEMPERATURE($propertyId: Int!, $roomId: Int!, $temperature: Float!) { setRoomAdjustTemperature(input: {propertyId: $propertyId, roomId: $roomId, temperature: $temperature}) { id adjustTemperature { active endDateTime temperature __typename } __typename } }",
|
||||
}
|
||||
|
||||
// Convertir les données en JSON
|
||||
requestDataJSON, err := json.Marshal(requestData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Créer la requête HTTP
|
||||
req, err := http.NewRequest(http.MethodPost, graphqlAPIURL, bytes.NewBuffer(requestDataJSON))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := Do[map[string]interface{}](c, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Println("SetRoomTemperature Resp", resp)
|
||||
|
||||
return c.GetRoom(propertyID, roomID)
|
||||
}
|
Loading…
Reference in New Issue
Block a user