├── go.mod ├── LICENSE ├── README.md └── gotapo.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/KusoKaihatsuSha/gotapo 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 クソ開発者 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 2 | [![godoc](https://godoc.org/github.com/KusoKaihatsuSha/gotapo?status.svg)](https://godoc.org/github.com/KusoKaihatsuSha/gotapo) 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/KusoKaihatsuSha/gotapo)](https://goreportcard.com/report/github.com/KusoKaihatsuSha/gotapo) 4 | 5 | # Source package for remoting TAPO Cameras (C200, C210, C220, C310 and other) 6 | > Tested on model cameras TP Link Tapo C200/C210/C310 7 | 8 | > !!! Caution with update your cameras to new drivers. May be problems with using unofficial apps. 9 | 10 | > !!! Unofficial method of using cameras TP Link Tapo C200/C310 11 | 12 | ### Functionality 13 | 14 | - Move camera (C2XX) 15 | - Turn next preset (C2XX) 16 | - Switch night mode 17 | - Switch detect mode 18 | - Switch alarm mode (flash, sound) 19 | - Switch auto-tracking 20 | - Switch led 21 | - Edit OSD 22 | - Flip camera 23 | - some other 24 | 25 | ### Add to use 26 | 27 | ``` 28 | go get github.com/KusoKaihatsuSha/gotapo.git 29 | ``` 30 | 31 | ### Sample 32 | 33 | ``` 34 | git clone https://github.com/KusoKaihatsuSha/appgotapo.git 35 | ``` 36 | 37 | -------------------------------------------------------------------------------- /gotapo.go: -------------------------------------------------------------------------------- 1 | // Package gotapo working 2 | // with camera tapo like c200, c310 3 | // by http 4 | package gotapo 5 | 6 | import ( 7 | "bytes" 8 | "crypto/aes" 9 | "crypto/cipher" 10 | "crypto/md5" 11 | "crypto/sha256" 12 | "crypto/tls" 13 | "encoding/base64" 14 | "encoding/json" 15 | "fmt" 16 | "io" 17 | "net/http" 18 | "os" 19 | "path/filepath" 20 | "reflect" 21 | "strconv" 22 | "strings" 23 | "time" 24 | ) 25 | 26 | // helper func 27 | var p = fmt.Println 28 | 29 | const ( 30 | // MethodGet as link to methods 31 | MethodGet = "get" 32 | 33 | // MethodSet as link to methods 34 | MethodSet = "set" 35 | 36 | // MethodMR as link to methods 37 | MethodMR = "multipleRequest" 38 | 39 | // MethodDo as link to methods 40 | MethodDo = "do" 41 | 42 | // MethodLogin as link to methods 43 | MethodLogin = "login" 44 | 45 | EncryptType = "3" 46 | ) 47 | 48 | var ( 49 | 50 | // DefXBool need for Postfix xBool 51 | DefXBool = "Def" 52 | ) 53 | 54 | // Types type of unnormal get bool value 55 | type Types struct { 56 | Default string 57 | Head string 58 | Next string 59 | isBinary bool 60 | isCommand bool 61 | isSpecial string 62 | isTrue bool 63 | isFalse bool 64 | } 65 | 66 | // Stages type of unnormal get bool value. (with vals) 67 | type Stages struct { 68 | Next string `🟢:"🔴" 🔴:"🟢" +:"-" -:"+" on:"off" off:"on"` 69 | Binary string `-:"true" +:"true" 🟢:"true" 🔴:"true" on:"true" off:"true"` 70 | True string `+:"true" 🟢:"true" on:"true"` 71 | False string `-:"true" 🔴:"true" off:"true"` 72 | Command string `!:"true" @:"true" 🟢:"true" 🔴:"true" 🔵:"true" -:"true" +:"true" /:"true"` 73 | Special string `!:"json" @:"exec"` 74 | sBool1 string `1:"🟢" 0:"🔴" 2:"🔵"` 75 | sBool2 string `1:"+" 0:"-" 2:"/"` 76 | sBoolDef string `1:"on" 0:"off" 2:"/"` 77 | } 78 | 79 | // presets type of Id Presets of cam 80 | type presets struct { 81 | ID string 82 | Name string 83 | } 84 | 85 | // elements type of Elements of cam 86 | type elements struct { 87 | NightMode *child 88 | NightModeAuto *child 89 | PrivacyMode *child 90 | Indicator *child 91 | DetectMode *child 92 | AutotrackingMode *child 93 | AlarmMode *child 94 | ImageCorrection *child 95 | ImageFlip *child 96 | MoveX string 97 | MoveY string 98 | AlarmModeUpdateFlash *child 99 | AlarmModeUpdateSound *child 100 | DetectPersonMode *child 101 | DetectModeUpdateSens *child 102 | } 103 | 104 | // settings type of Settings of cam 105 | type settings struct { 106 | PrintImageSettings *child 107 | PrintImageSettings2 *child 108 | Time *child 109 | PresetChangeOsd *child 110 | VisibleOsdTime *child 111 | VisibleOsdText *child 112 | OsdText string 113 | DetectSensitivity int 114 | DetectSoundAlternativeMode *child 115 | DetectEnableSound *child 116 | DetectEnableFlash *child 117 | Move *child 118 | } 119 | 120 | // child assignment of function 121 | type child struct { 122 | Value bool 123 | run func() 124 | } 125 | 126 | // Tapo is general type with Vals 127 | type Tapo struct { 128 | Parameters map[string]string 129 | Host string 130 | Port string 131 | UserDef string 132 | User string 133 | Password string 134 | UserID string 135 | Rotate bool 136 | FishEye bool 137 | Flip bool 138 | stokID string 139 | TimeStr string 140 | userGroup string 141 | hashedPassword string 142 | hashedPasswordMD5 string 143 | hashedPasswordSha256 string 144 | hostURL string 145 | hostURLStok string 146 | deviceModel string 147 | deviceID string 148 | presets []*presets 149 | lastPosition int 150 | LastFile string 151 | Elements *elements 152 | Settings *settings 153 | NextPreset func() 154 | Reboot func() 155 | InsecureAuth bool 156 | Iv []byte 157 | Key []byte 158 | Seq string 159 | Encrypt bool 160 | } 161 | 162 | // Action is general Action cam 163 | type Action interface { 164 | On() 165 | Off() 166 | } 167 | 168 | // updateStok type for upd key 169 | type updateStok struct { 170 | Method string `json:"method"` 171 | Params struct { 172 | Hashed bool `json:"hashed"` 173 | Username string `json:"username"` 174 | Password string `json:"password"` 175 | } `json:"params"` 176 | } 177 | 178 | // updateStokReturn type for upd key return 179 | type updateStokReturn struct { 180 | ErrorCode int64 `json:"error_code"` 181 | Result struct { 182 | Stok string `json:"stok"` 183 | UserGroup string `json:"user_group"` 184 | StartSeq *int `json:"start_seq"` 185 | } `json:"result"` 186 | } 187 | 188 | // device type for device cam 189 | type device struct { 190 | Method string `json:"method"` 191 | DeviceInfo struct { 192 | Name []string `json:"name"` 193 | } `json:"device_info"` 194 | } 195 | 196 | // deviceRet type for device cam return 197 | type deviceRet struct { 198 | Result struct { 199 | Responses []struct { 200 | Method string `json:"method"` 201 | Result struct { 202 | DeviceInfo struct { 203 | BasicInfo struct { 204 | Ffs bool `json:"ffs"` 205 | DeviceType string `json:"device_type"` 206 | DeviceModel string `json:"device_model"` 207 | DeviceName string `json:"device_name"` 208 | DeviceInfo string `json:"device_info"` 209 | HwVersion string `json:"hw_version"` 210 | SwVersion string `json:"sw_version"` 211 | DeviceAlias string `json:"device_alias"` 212 | Features string `json:"features"` 213 | Barcode string `json:"barcode"` 214 | Mac string `json:"mac"` 215 | DevID string `json:"dev_id"` 216 | OemID string `json:"oem_id"` 217 | HwDesc string `json:"hw_desc"` 218 | } `json:"basic_info"` 219 | } `json:"device_info"` 220 | } `json:"result"` 221 | ErrorCode int `json:"error_code"` 222 | } `json:"responses"` 223 | } `json:"result"` 224 | ErrorCode int `json:"error_code"` 225 | } 226 | 227 | type moveTo struct { 228 | Method string `json:"method"` 229 | Motor struct { 230 | MoveStep struct { 231 | Derection string `json:"direction"` 232 | } `json:"movestep"` 233 | } `json:"motor"` 234 | } 235 | 236 | // movePosition type for moving cam 237 | type movePosition struct { 238 | Method string `json:"method"` 239 | Motor struct { 240 | Move struct { 241 | YCoord string `json:"y_coord"` 242 | XCoord string `json:"x_coord"` 243 | } `json:"move"` 244 | } `json:"motor"` 245 | } 246 | 247 | // presetList type for moving presets cam 248 | type presetList struct { 249 | Method string `json:"method"` 250 | Preset struct { 251 | Name []string `json:"name"` 252 | } `json:"preset"` 253 | } 254 | 255 | // presetListReturn type for moving presets cam return 256 | type presetListReturn struct { 257 | Result struct { 258 | Responses []struct { 259 | Method string `json:"method"` 260 | Result struct { 261 | Preset struct { 262 | Preset struct { 263 | ID []string `json:"id"` 264 | Name []string `json:"name"` 265 | PositionPan []string `json:"position_pan"` 266 | PositionTilt []string `json:"position_tilt"` 267 | ReadOnly []string `json:"read_only"` 268 | } `json:"preset"` 269 | } `json:"preset"` 270 | } `json:"result"` 271 | ErrorCode int `json:"error_code"` 272 | } `json:"responses"` 273 | } `json:"result"` 274 | ErrorCode int `json:"error_code"` 275 | } 276 | 277 | // nextPreset type for moving next preset 278 | type nextPreset struct { 279 | Method string `json:"method"` 280 | Preset struct { 281 | GotoPreset struct { 282 | ID string `json:"id"` 283 | } `json:"goto_preset"` 284 | } `json:"preset"` 285 | } 286 | 287 | // reboot type for rebooting 288 | type reboot struct { 289 | Method string `json:"method"` 290 | System struct { 291 | Reboot string `json:"reboot"` 292 | } `json:"system"` 293 | } 294 | 295 | // alarm type for alarming 296 | type alarm struct { 297 | Method string `json:"method"` 298 | MsgAlarm struct { 299 | Chn1MsgAlarmInfo struct { 300 | AlarmType string `json:"alarm_type"` 301 | Enabled string `json:"enabled"` 302 | LightType string `json:"light_type"` 303 | AlarmMode []string `json:"alarm_mode"` 304 | } `json:"chn1_msg_alarm_info"` 305 | } `json:"msg_alarm"` 306 | } 307 | 308 | // led type for led state 309 | type led struct { 310 | Method string `json:"method"` 311 | Led struct { 312 | Config struct { 313 | Enabled string `json:"enabled"` 314 | } `json:"config"` 315 | } `json:"led"` 316 | } 317 | 318 | // detect type for detect state 319 | type detect struct { 320 | Method string `json:"method"` 321 | MotionDetection struct { 322 | MotionDet struct { 323 | Enabled string `json:"enabled"` 324 | DigitalSensitivity string `json:"digital_sensitivity"` 325 | } `json:"motion_det"` 326 | } `json:"motion_detection"` 327 | } 328 | 329 | // type privacy state 330 | type privacy struct { 331 | Method string `json:"method"` 332 | LensMask struct { 333 | LensMaskInfo struct { 334 | Enabled string `json:"enabled"` 335 | } `json:"lens_mask_info"` 336 | } `json:"lens_mask"` 337 | } 338 | 339 | // type nightmode state 340 | type nightMode struct { 341 | Method string `json:"method"` 342 | Image struct { 343 | Common struct { 344 | InfType string `json:"inf_type"` 345 | } `json:"common"` 346 | } `json:"image"` 347 | } 348 | 349 | // type autotracking state 350 | type autotracking struct { 351 | Method string `json:"method"` 352 | TargetTrack struct { 353 | TargetTrackInfo struct { 354 | Enabled string `json:"enabled"` 355 | } `json:"target_track_info"` 356 | } `json:"target_track"` 357 | } 358 | 359 | // type get settings 360 | type getImageSettings struct { 361 | Method string `json:"method"` 362 | Image struct { 363 | Name string `json:"name"` 364 | } `json:"image"` 365 | } 366 | 367 | // type get settings return 368 | type getImageSettingsRet struct { 369 | Result struct { 370 | Responses []struct { 371 | Method string `json:"method"` 372 | Result struct { 373 | Image struct { 374 | Switch struct { 375 | Name string `json:".name"` 376 | Type string `json:".type"` 377 | SwitchMode string `json:"switch_mode"` 378 | ScheduleStartTime string `json:"schedule_start_time"` 379 | ScheduleEndTime string `json:"schedule_end_time"` 380 | FlipType string `json:"flip_type"` 381 | RotateType string `json:"rotate_type"` 382 | Ldc string `json:"ldc"` 383 | NightVisionMode string `json:"night_vision_mode"` 384 | WtlIntensityLevel string `json:"wtl_intensity_level"` 385 | } `json:"switch"` 386 | Common struct { 387 | Name string `json:".name"` 388 | Type string `json:".type"` 389 | Luma string `json:"luma"` 390 | Contrast string `json:"contrast"` 391 | Chroma string `json:"chroma"` 392 | Saturation string `json:"saturation"` 393 | Sharpness string `json:"sharpness"` 394 | ExpType string `json:"exp_type"` 395 | Shutter string `json:"shutter"` 396 | FocusType string `json:"focus_type"` 397 | FocusLimited string `json:"focus_limited"` 398 | ExpGain string `json:"exp_gain"` 399 | InfStartTime string `json:"inf_start_time"` 400 | InfEndTime string `json:"inf_end_time"` 401 | InfSensitivity string `json:"inf_sensitivity"` 402 | InfDelay string `json:"inf_delay"` 403 | WideDynamic string `json:"wide_dynamic"` 404 | LightFreqMode string `json:"light_freq_mode"` 405 | WdGain string `json:"wd_gain"` 406 | WbType string `json:"wb_type"` 407 | WbRGain string `json:"wb_R_gain"` 408 | WbGGain string `json:"wb_G_gain"` 409 | WbBGain string `json:"wb_B_gain"` 410 | LockRedGain string `json:"lock_red_gain"` 411 | LockGrGain string `json:"lock_gr_gain"` 412 | LockGbGain string `json:"lock_gb_gain"` 413 | LockBlueGain string `json:"lock_blue_gain"` 414 | LockRedColton string `json:"lock_red_colton"` 415 | LockGreenColton string `json:"lock_green_colton"` 416 | LockBlueColton string `json:"lock_blue_colton"` 417 | LockSource string `json:"lock_source"` 418 | AreaCompensation string `json:"area_compensation"` 419 | Smartir string `json:"smartir"` 420 | SmartirLevel string `json:"smartir_level"` 421 | HighLightCompensation string `json:"high_light_compensation"` 422 | Dehaze string `json:"dehaze"` 423 | InfType string `json:"inf_type"` 424 | } `json:"common"` 425 | } `json:"image"` 426 | } `json:"result"` 427 | ErrorCode int `json:"error_code"` 428 | } `json:"responses"` 429 | } `json:"result"` 430 | ErrorCode int `json:"error_code"` 431 | } 432 | 433 | // type image settings fish eye 434 | type setImageCorrection struct { 435 | Method string `json:"method"` 436 | Image struct { 437 | Switch struct { 438 | Ldc string `json:"ldc"` 439 | } `json:"switch"` 440 | } `json:"image"` 441 | } 442 | 443 | // type flip 444 | type setImageFlip struct { 445 | Method string `json:"method"` 446 | Image struct { 447 | Switch struct { 448 | FlipType string `json:"flip_type"` 449 | } `json:"switch"` 450 | } `json:"image"` 451 | } 452 | 453 | // type get Time 454 | type getTime struct { 455 | Method string `json:"method"` 456 | System struct { 457 | Name []string `json:"name"` 458 | } `json:"system"` 459 | } 460 | 461 | // type get Time return 462 | type getTimeRet struct { 463 | System struct { 464 | ClockStatus struct { 465 | SecondsFrom1970 int `json:"seconds_from_1970"` 466 | LocalTime string `json:"local_time"` 467 | } `json:"clock_status"` 468 | } `json:"system"` 469 | ErrorCode int `json:"error_code"` 470 | } 471 | 472 | // type working with OSD return 473 | type getOSDRet struct { 474 | OSD struct { 475 | Date struct { 476 | Name string `json:".name"` 477 | Type string `json:".type"` 478 | XCoor string `json:"x_coor"` 479 | YCoor string `json:"y_coor"` 480 | Enabled string `json:"enabled"` 481 | } `json:"date"` 482 | Week struct { 483 | Name string `json:".name"` 484 | Type string `json:".type"` 485 | Enabled string `json:"enabled"` 486 | XCoor string `json:"x_coor"` 487 | YCoor string `json:"y_coor"` 488 | } `json:"week"` 489 | Font struct { 490 | Name string `json:".name"` 491 | Type string `json:".type"` 492 | Display string `json:"display"` 493 | Size string `json:"size"` 494 | ColorType string `json:"color_type"` 495 | Color string `json:"color"` 496 | } `json:"font"` 497 | LabelInfo []struct { 498 | LabelInfo1 struct { 499 | Name string `json:".name"` 500 | Type string `json:".type"` 501 | XCoor string `json:"x_coor"` 502 | YCoor string `json:"y_coor"` 503 | Enabled string `json:"enabled"` 504 | Text string `json:"text"` 505 | } `json:"label_info_1,omitempty"` 506 | LabelInfo2 struct { 507 | Name string `json:".name"` 508 | Type string `json:".type"` 509 | Enabled string `json:"enabled"` 510 | Text string `json:"text"` 511 | XCoor string `json:"x_coor"` 512 | YCoor string `json:"y_coor"` 513 | } `json:"label_info_2,omitempty"` 514 | LabelInfo3 struct { 515 | Name string `json:".name"` 516 | Type string `json:".type"` 517 | Enabled string `json:"enabled"` 518 | Text string `json:"text"` 519 | XCoor string `json:"x_coor"` 520 | YCoor string `json:"y_coor"` 521 | } `json:"label_info_3,omitempty"` 522 | } `json:"label_info"` 523 | } `json:"OSD"` 524 | ErrorCode int `json:"error_code"` 525 | } 526 | 527 | // type working with OSD 528 | type getOSD struct { 529 | Method string `json:"method"` 530 | Data struct { 531 | Name []string `json:"name"` 532 | Table []string `json:"table"` 533 | } `json:"OSD"` 534 | } 535 | 536 | type setLed struct { 537 | Method string `json:"method"` 538 | Data struct { 539 | Type struct { 540 | Value string `json:"enabled"` 541 | } `json:"config"` 542 | } `json:"led"` 543 | } 544 | 545 | type setPersonDetect struct { 546 | Method string `json:"method"` 547 | Data struct { 548 | Type struct { 549 | Value string `json:"enabled"` 550 | } `json:"detection"` 551 | } `json:"people_detection"` 552 | } 553 | 554 | type deviceInfo struct { 555 | Method string `json:"method"` 556 | Data struct { 557 | Type struct { 558 | Name []string `json:"name"` 559 | } `json:"device_info"` 560 | } `json:"params"` 561 | } 562 | type detectionConfig struct { 563 | Method string `json:"method"` 564 | Data struct { 565 | Type struct { 566 | Name []string `json:"name"` 567 | } `json:"motion_detection"` 568 | } `json:"params"` 569 | } 570 | type detectionConfigResponse struct { 571 | Result struct { 572 | Responses []struct { 573 | Method string `json:"method"` 574 | Result struct { 575 | MotionDetection struct { 576 | MotionDet struct { 577 | Name string `json:".name"` 578 | Type string `json:".type"` 579 | Sensitivity string `json:"sensitivity"` 580 | DigitalSensitivity string `json:"digital_sensitivity"` 581 | Enabled string `json:"enabled"` 582 | } `json:"motion_det"` 583 | } `json:"motion_detection"` 584 | } `json:"result"` 585 | ErrorCode int `json:"error_code"` 586 | } `json:"responses"` 587 | } `json:"result"` 588 | ErrorCode int `json:"error_code"` 589 | } 590 | type personDetectionConfig struct { 591 | Method string `json:"method"` 592 | Data struct { 593 | Type struct { 594 | Name []string `json:"name"` 595 | } `json:"people_detection"` 596 | } `json:"params"` 597 | } 598 | type vehicleDetectionConfig struct { 599 | Method string `json:"method"` 600 | Data struct { 601 | Type struct { 602 | Name []string `json:"name"` 603 | } `json:"vehicle_detection"` 604 | } `json:"params"` 605 | } 606 | type bcdConfig struct { 607 | Method string `json:"method"` 608 | Data struct { 609 | Type struct { 610 | Name []string `json:"name"` 611 | } `json:"sound_detection"` 612 | } `json:"params"` 613 | } 614 | type petDetectionConfig struct { 615 | Method string `json:"method"` 616 | Data struct { 617 | Type struct { 618 | Name []string `json:"name"` 619 | } `json:"pet_detection"` 620 | } `json:"params"` 621 | } 622 | type barkDetectionConfig struct { 623 | Method string `json:"method"` 624 | Data struct { 625 | Type struct { 626 | Name []string `json:"name"` 627 | } `json:"bark_detection"` 628 | } `json:"params"` 629 | } 630 | type meowDetectionConfig struct { 631 | Method string `json:"method"` 632 | Data struct { 633 | Type struct { 634 | Name []string `json:"name"` 635 | } `json:"meow_detection"` 636 | } `json:"params"` 637 | } 638 | type glassDetectionConfig struct { 639 | Method string `json:"method"` 640 | Data struct { 641 | Type struct { 642 | Name []string `json:"name"` 643 | } `json:"glass_detection"` 644 | } `json:"params"` 645 | } 646 | type tamperDetectionConfig struct { 647 | Method string `json:"method"` 648 | Data struct { 649 | Type struct { 650 | Name string `json:"name"` 651 | } `json:"tamper_detection"` 652 | } `json:"params"` 653 | } 654 | type lensMaskConfig struct { 655 | Method string `json:"method"` 656 | Data struct { 657 | Type struct { 658 | Name []string `json:"name"` 659 | } `json:"lens_mask"` 660 | } `json:"params"` 661 | } 662 | type ldc struct { 663 | Method string `json:"method"` 664 | Data struct { 665 | Type struct { 666 | Name []string `json:"name"` 667 | } `json:"image"` 668 | } `json:"params"` 669 | } 670 | type lastAlarmInfo struct { 671 | Method string `json:"method"` 672 | Data struct { 673 | Type struct { 674 | Name []string `json:"name"` 675 | } `json:"msg_alarm"` 676 | } `json:"params"` 677 | } 678 | 679 | type lastAlarmInfoResponse struct { 680 | Result struct { 681 | Responses []struct { 682 | Method string `json:"method"` 683 | Result struct { 684 | MsgAlarm struct { 685 | Chn1MsgAlarmInfo struct { 686 | Name string `json:".name"` 687 | Type string `json:".type"` 688 | SoundAlarmEnabled string `json:"sound_alarm_enabled"` 689 | LightAlarmEnabled string `json:"light_alarm_enabled"` 690 | AlarmType string `json:"alarm_type"` 691 | LightType string `json:"light_type"` 692 | AlarmMode []string `json:"alarm_mode"` 693 | Enabled string `json:"enabled"` 694 | } `json:"chn1_msg_alarm_info"` 695 | } `json:"msg_alarm"` 696 | } `json:"result"` 697 | ErrorCode int `json:"error_code"` 698 | } `json:"responses"` 699 | } `json:"result"` 700 | ErrorCode int `json:"error_code"` 701 | } 702 | 703 | type ledStatus struct { 704 | Method string `json:"method"` 705 | Data struct { 706 | Type struct { 707 | Name []string `json:"name"` 708 | } `json:"led"` 709 | } `json:"params"` 710 | } 711 | type targetTrackConfig struct { 712 | Method string `json:"method"` 713 | Data struct { 714 | Type struct { 715 | Name []string `json:"name"` 716 | } `json:"target_track"` 717 | } `json:"params"` 718 | } 719 | type presetConfig struct { 720 | Method string `json:"method"` 721 | Data struct { 722 | Type struct { 723 | Name []string `json:"name"` 724 | } `json:"preset"` 725 | } `json:"params"` 726 | } 727 | type firmwareUpdateStatus struct { 728 | Method string `json:"method"` 729 | Data struct { 730 | Type struct { 731 | Name []string `json:"name"` 732 | } `json:"cloud_config"` 733 | } `json:"params"` 734 | } 735 | type mediaEncrypt struct { 736 | Method string `json:"method"` 737 | Data struct { 738 | Type struct { 739 | Name []string `json:"name"` 740 | } `json:"cet"` 741 | } `json:"params"` 742 | } 743 | type connectionType struct { 744 | Method string `json:"method"` 745 | Data struct { 746 | Type struct { 747 | Name []string `json:"get_connection_type"` 748 | } `json:"network"` 749 | } `json:"params"` 750 | } 751 | type alarmConfig struct { 752 | Method string `json:"method"` 753 | Data struct { 754 | Type struct { 755 | } `json:"msg_alarm"` 756 | } `json:"params"` 757 | } 758 | type alarmPlan struct { 759 | Method string `json:"method"` 760 | Data struct { 761 | Type struct { 762 | } `json:"msg_alarm_plan"` 763 | } `json:"params"` 764 | } 765 | type sirenTypeList struct { 766 | Method string `json:"method"` 767 | Data struct { 768 | Type struct { 769 | } `json:"msg_alarm"` 770 | } `json:"params"` 771 | } 772 | type lightTypeList struct { 773 | Method string `json:"method"` 774 | Data struct { 775 | Type struct { 776 | } `json:"msg_alarm"` 777 | } `json:"params"` 778 | } 779 | type sirenStatus struct { 780 | Method string `json:"method"` 781 | Data struct { 782 | Type struct { 783 | } `json:"msg_alarm"` 784 | } `json:"params"` 785 | } 786 | type lightFrequencyInfo struct { 787 | Method string `json:"method"` 788 | Data struct { 789 | Type struct { 790 | Name string `json:"name"` 791 | } `json:"image"` 792 | } `json:"params"` 793 | } 794 | type lightFrequencyCapability struct { 795 | Method string `json:"method"` 796 | Data struct { 797 | Type struct { 798 | Name string `json:"name"` 799 | } `json:"image"` 800 | } `json:"params"` 801 | } 802 | type childDeviceList struct { 803 | Method string `json:"method"` 804 | Data struct { 805 | Type struct { 806 | StartIndex int `json:"start_index"` 807 | } `json:"childControl"` 808 | } `json:"params"` 809 | } 810 | type rotationStatus struct { 811 | Method string `json:"method"` 812 | Data struct { 813 | Type struct { 814 | Name []string `json:"name"` 815 | } `json:"image"` 816 | } `json:"params"` 817 | } 818 | type nightVisionModeConfig struct { 819 | Method string `json:"method"` 820 | Data struct { 821 | Type struct { 822 | Name string `json:"name"` 823 | } `json:"image"` 824 | } `json:"params"` 825 | } 826 | type whitelampStatus struct { 827 | Method string `json:"method"` 828 | Data struct { 829 | Type struct { 830 | GetWtlStatus []string `json:"get_wtl_status"` 831 | } `json:"image"` 832 | } `json:"params"` 833 | } 834 | type whitelampConfig struct { 835 | Method string `json:"method"` 836 | Data struct { 837 | Type struct { 838 | Name string `json:"name"` 839 | } `json:"image"` 840 | } `json:"params"` 841 | } 842 | type msgPushConfig struct { 843 | Method string `json:"method"` 844 | Data struct { 845 | Type struct { 846 | Name []string `json:"name"` 847 | } `json:"msg_push"` 848 | } `json:"params"` 849 | } 850 | type sdCardStatus struct { 851 | Method string `json:"method"` 852 | Data struct { 853 | Type struct { 854 | Table []string `json:"table"` 855 | } `json:"harddisk_manage"` 856 | } `json:"params"` 857 | } 858 | type circularRecordingConfig struct { 859 | Method string `json:"method"` 860 | Data struct { 861 | Type struct { 862 | Name string `json:"name"` 863 | } `json:"harddisk_manage"` 864 | } `json:"params"` 865 | } 866 | type recordPlan struct { 867 | Method string `json:"method"` 868 | Data struct { 869 | Type struct { 870 | Name []string `json:"name"` 871 | } `json:"record_plan"` 872 | } `json:"params"` 873 | } 874 | type firmwareAutoUpgradeConfig struct { 875 | Method string `json:"method"` 876 | Data struct { 877 | Type struct { 878 | Name []string `json:"name"` 879 | } `json:"auto_upgrade"` 880 | } `json:"params"` 881 | } 882 | 883 | // type working with OSD 884 | type osd struct { 885 | Method string `json:"method"` 886 | OSD struct { 887 | Date struct { 888 | Enabled string `json:"enabled"` 889 | XCoor int `json:"x_coor"` 890 | YCoor int `json:"y_coor"` 891 | } `json:"date"` 892 | Week struct { 893 | Enabled string `json:"enabled"` 894 | XCoor int `json:"x_coor"` 895 | YCoor int `json:"y_coor"` 896 | } `json:"week"` 897 | Font struct { 898 | Color string `json:"color"` 899 | ColorType string `json:"color_type"` 900 | Display string `json:"display"` 901 | Size string `json:"size"` 902 | } `json:"font"` 903 | LabelInfo1 struct { 904 | Enabled string `json:"enabled"` 905 | Text string `json:"text"` 906 | XCoor int `json:"x_coor"` 907 | YCoor int `json:"y_coor"` 908 | } `json:"label_info_1"` 909 | } `json:"OSD"` 910 | } 911 | 912 | type queryResponse struct { 913 | ErrorCode int `json:"error_code"` 914 | Seq int `json:"seq"` 915 | Result struct { 916 | Response string `json:"response"` 917 | } `json:"result"` 918 | } 919 | 920 | type many struct { 921 | Method string `json:"method"` 922 | Params struct { 923 | Requests []any `json:"requests"` 924 | } `json:"params"` 925 | } 926 | 927 | type secure struct { 928 | Method string `json:"method"` 929 | Params struct { 930 | Request string `json:"request"` 931 | } `json:"params"` 932 | } 933 | 934 | // Auth with nonce usage 935 | type loginInsecure struct { 936 | Method string `json:"method"` 937 | Params struct { 938 | Cnonce string `json:"cnonce"` 939 | EncryptType string `json:"encrypt_type"` 940 | DigestPasswd string `json:"digest_passwd,omitempty"` 941 | Username string `json:"username"` 942 | } `json:"params"` 943 | } 944 | 945 | // Auth with nonce usage 946 | type loginInsecureResponse struct { 947 | ErrorCode int `json:"error_code"` 948 | Result struct { 949 | Data struct { 950 | Code int `json:"code"` 951 | EncryptType []string `json:"encrypt_type"` 952 | Key string `json:"key"` 953 | Nonce string `json:"nonce"` 954 | DeviceConfirm string `json:"device_confirm"` 955 | } `json:"data"` 956 | } `json:"result"` 957 | } 958 | 959 | // nil func 960 | func fnil() { 961 | } 962 | 963 | func secureTemplate(values ...any) secure { 964 | t := secure{} 965 | t.Method = "securePassthrough" 966 | t.Params.Request = values[0].(string) 967 | return t 968 | } 969 | 970 | func manyTemplate(values ...any) many { 971 | t := many{} 972 | t.Method = "multipleRequest" 973 | t.Params.Requests = append(t.Params.Requests, values...) 974 | return t 975 | } 976 | 977 | func osdTemplate(values ...any) osd { 978 | t := osd{} 979 | t.Method = MethodSet 980 | t.OSD.Date.Enabled = values[0].(string) 981 | t.OSD.Date.XCoor = 0 982 | t.OSD.Date.YCoor = 0 983 | t.OSD.Font.Color = "white" 984 | t.OSD.Font.ColorType = "auto" 985 | t.OSD.Font.Display = "ntnb" 986 | t.OSD.Font.Size = "auto" 987 | t.OSD.LabelInfo1.Enabled = values[1].(string) 988 | t.OSD.LabelInfo1.Text = values[2].(string) 989 | t.OSD.LabelInfo1.XCoor = 0 990 | t.OSD.LabelInfo1.YCoor = 450 991 | //---china weeks--- 992 | t.OSD.Week.Enabled = new(Types).xBool(false).Default 993 | t.OSD.Week.XCoor = 0 994 | t.OSD.Week.YCoor = 0 995 | return t 996 | } 997 | 998 | func alarmTemplate(values ...any) alarm { 999 | t := alarm{} 1000 | t.Method = MethodSet 1001 | t.MsgAlarm.Chn1MsgAlarmInfo.AlarmType = values[0].(string) 1002 | t.MsgAlarm.Chn1MsgAlarmInfo.LightType = "1" 1003 | t.MsgAlarm.Chn1MsgAlarmInfo.AlarmMode = values[1].([]string) 1004 | t.MsgAlarm.Chn1MsgAlarmInfo.Enabled = values[2].(string) 1005 | return t 1006 | } 1007 | 1008 | func nextPresetTemplate(values ...any) nextPreset { 1009 | t := nextPreset{} 1010 | t.Method = MethodDo 1011 | t.Preset.GotoPreset.ID = values[0].(string) 1012 | return t 1013 | } 1014 | 1015 | func loginNewTemplate(values ...any) loginInsecure { 1016 | t := loginInsecure{} 1017 | t.Method = MethodLogin 1018 | t.Params.Username = values[0].(string) 1019 | t.Params.EncryptType = EncryptType 1020 | t.Params.DigestPasswd = values[1].(string) 1021 | return t 1022 | } 1023 | 1024 | func updateStokTemplate(values ...any) updateStok { 1025 | t := updateStok{} 1026 | t.Method = MethodLogin 1027 | t.Params.Hashed = true 1028 | t.Params.Username = values[0].(string) 1029 | t.Params.Password = values[1].(string) 1030 | return t 1031 | } 1032 | 1033 | func getTimeTemplate(values ...any) getTime { 1034 | t := getTime{} 1035 | t.Method = MethodGet 1036 | t.System.Name = []string{"clock_status"} 1037 | return t 1038 | } 1039 | 1040 | func setImageCorrectionTemplate(values ...any) setImageCorrection { 1041 | t := setImageCorrection{} 1042 | t.Method = MethodSet 1043 | t.Image.Switch.Ldc = values[0].(string) 1044 | return t 1045 | } 1046 | 1047 | func setImageFlipTemplate(values ...any) setImageFlip { 1048 | t := setImageFlip{} 1049 | t.Method = MethodSet 1050 | t.Image.Switch.FlipType = values[0].(string) 1051 | return t 1052 | } 1053 | 1054 | func detectTemplate(values ...any) detect { 1055 | t := detect{} 1056 | t.Method = MethodSet 1057 | switch values[1].(int) { 1058 | case 1: 1059 | t.MotionDetection.MotionDet.DigitalSensitivity = "20" 1060 | case 2: 1061 | t.MotionDetection.MotionDet.DigitalSensitivity = "50" 1062 | case 3: 1063 | t.MotionDetection.MotionDet.DigitalSensitivity = "80" 1064 | default: 1065 | t.MotionDetection.MotionDet.DigitalSensitivity = "20" 1066 | } 1067 | t.MotionDetection.MotionDet.Enabled = values[0].(string) 1068 | return t 1069 | } 1070 | 1071 | func privacyTemplate(values ...any) privacy { 1072 | t := privacy{} 1073 | t.Method = MethodSet 1074 | t.LensMask.LensMaskInfo.Enabled = values[0].(string) 1075 | return t 1076 | } 1077 | 1078 | func nightModeTemplate(values ...any) nightMode { 1079 | t := nightMode{} 1080 | t.Method = MethodSet 1081 | t.Image.Common.InfType = values[0].(string) 1082 | return t 1083 | } 1084 | 1085 | func autotrackingTemplate(values ...any) autotracking { 1086 | t := autotracking{} 1087 | t.Method = MethodSet 1088 | t.TargetTrack.TargetTrackInfo.Enabled = values[0].(string) 1089 | return t 1090 | } 1091 | 1092 | func getOSDTemplate(values ...any) getOSD { 1093 | t := getOSD{} 1094 | t.Method = MethodGet 1095 | t.Data.Name = []string{"date", "week", "font"} 1096 | t.Data.Table = []string{"label_info"} 1097 | return t 1098 | } 1099 | 1100 | func loginInitTemplate(values ...any) loginInsecure { 1101 | t := loginInsecure{} 1102 | t.Method = MethodLogin 1103 | t.Params.Username = values[0].(string) 1104 | t.Params.EncryptType = EncryptType 1105 | return t 1106 | } 1107 | 1108 | func rebootTemplate(values ...any) reboot { 1109 | t := reboot{} 1110 | t.Method = MethodDo 1111 | t.System.Reboot = "null" 1112 | return t 1113 | } 1114 | 1115 | func moveToTemplate(values ...any) moveTo { 1116 | t := moveTo{} 1117 | t.Method = MethodDo 1118 | t.Motor.MoveStep.Derection = values[0].(string) 1119 | return t 1120 | } 1121 | 1122 | func movePositionTemplate(values ...any) movePosition { 1123 | t := movePosition{} 1124 | t.Method = MethodDo 1125 | t.Motor.Move.XCoord = strconv.Itoa(values[0].(int)) 1126 | t.Motor.Move.YCoord = strconv.Itoa(values[1].(int)) 1127 | return t 1128 | } 1129 | 1130 | func setLedTemplate(values ...any) setLed { 1131 | t := setLed{} 1132 | t.Method = MethodSet 1133 | t.Data.Type.Value = values[0].(string) 1134 | return t 1135 | } 1136 | 1137 | func setPersonDetectTemplate(values ...any) setPersonDetect { 1138 | t := setPersonDetect{} 1139 | t.Method = MethodSet 1140 | t.Data.Type.Value = values[0].(string) 1141 | return t 1142 | } 1143 | 1144 | func deviceInfoTemplate(values ...any) deviceInfo { 1145 | t := deviceInfo{} 1146 | t.Method = "getDeviceInfo" 1147 | t.Data.Type.Name = []string{"basic_info"} 1148 | return t 1149 | } 1150 | 1151 | func detectionConfigTemplate(values ...any) detectionConfig { 1152 | t := detectionConfig{} 1153 | t.Method = "getDetectionConfig" 1154 | t.Data.Type.Name = []string{"motion_det"} 1155 | return t 1156 | } 1157 | 1158 | func personDetectionConfigTemplate(values ...any) personDetectionConfig { 1159 | t := personDetectionConfig{} 1160 | t.Method = "getPersonDetectionConfig" 1161 | t.Data.Type.Name = []string{"detection"} 1162 | return t 1163 | } 1164 | 1165 | func vehicleDetectionConfigTemplate(values ...any) vehicleDetectionConfig { 1166 | t := vehicleDetectionConfig{} 1167 | t.Method = "getVehicleDetectionConfig" 1168 | t.Data.Type.Name = []string{"detection"} 1169 | return t 1170 | } 1171 | 1172 | func bcdConfigTemplate(values ...any) bcdConfig { 1173 | t := bcdConfig{} 1174 | t.Method = "getBCDConfig" 1175 | t.Data.Type.Name = []string{"bcd"} 1176 | return t 1177 | } 1178 | 1179 | func petDetectionConfigTemplate(values ...any) petDetectionConfig { 1180 | t := petDetectionConfig{} 1181 | t.Method = "getPetDetectionConfig" 1182 | t.Data.Type.Name = []string{"detection"} 1183 | return t 1184 | } 1185 | 1186 | func barkDetectionConfigTemplate(values ...any) barkDetectionConfig { 1187 | t := barkDetectionConfig{} 1188 | t.Method = "getBarkDetectionConfig" 1189 | t.Data.Type.Name = []string{"detection"} 1190 | return t 1191 | } 1192 | 1193 | func meowDetectionConfigTemplate(values ...any) meowDetectionConfig { 1194 | t := meowDetectionConfig{} 1195 | t.Method = "getMeowDetectionConfig" 1196 | t.Data.Type.Name = []string{"detection"} 1197 | return t 1198 | } 1199 | 1200 | func glassDetectionConfigTemplate(values ...any) glassDetectionConfig { 1201 | t := glassDetectionConfig{} 1202 | t.Method = "getGlassDetectionConfig" 1203 | t.Data.Type.Name = []string{"detection"} 1204 | return t 1205 | } 1206 | 1207 | func tamperDetectionConfigTemplate(values ...any) tamperDetectionConfig { 1208 | t := tamperDetectionConfig{} 1209 | t.Method = "getTamperDetectionConfig" 1210 | t.Data.Type.Name = "tamper_det" 1211 | return t 1212 | } 1213 | 1214 | func lensMaskConfigTemplate(values ...any) lensMaskConfig { 1215 | t := lensMaskConfig{} 1216 | t.Method = "getLensMaskConfig" 1217 | t.Data.Type.Name = []string{"lens_mask_info"} 1218 | return t 1219 | } 1220 | 1221 | func ldcTemplate(values ...any) ldc { 1222 | t := ldc{} 1223 | t.Method = "getLdc" 1224 | t.Data.Type.Name = []string{"switch", "common"} 1225 | return t 1226 | } 1227 | 1228 | func lastAlarmInfoTemplate(values ...any) lastAlarmInfo { 1229 | t := lastAlarmInfo{} 1230 | t.Method = "getLastAlarmInfo" 1231 | t.Data.Type.Name = []string{"chn1_msg_alarm_info"} 1232 | return t 1233 | } 1234 | 1235 | func ledStatusTemplate(values ...any) ledStatus { 1236 | t := ledStatus{} 1237 | t.Method = "getLedStatus" 1238 | t.Data.Type.Name = []string{"config"} 1239 | return t 1240 | } 1241 | 1242 | func targetTrackConfigTemplate(values ...any) targetTrackConfig { 1243 | t := targetTrackConfig{} 1244 | t.Method = "getTargetTrackConfig" 1245 | t.Data.Type.Name = []string{"target_track_info"} 1246 | return t 1247 | } 1248 | 1249 | func presetConfigTemplate(values ...any) presetConfig { 1250 | t := presetConfig{} 1251 | t.Method = "getPresetConfig" 1252 | t.Data.Type.Name = []string{"preset"} 1253 | return t 1254 | } 1255 | 1256 | func firmwareUpdateStatusTemplate(values ...any) firmwareUpdateStatus { 1257 | t := firmwareUpdateStatus{} 1258 | t.Method = "getFirmwareUpdateStatus" 1259 | t.Data.Type.Name = []string{"upgrade_status"} 1260 | return t 1261 | } 1262 | 1263 | func mediaEncryptTemplate(values ...any) mediaEncrypt { 1264 | t := mediaEncrypt{} 1265 | t.Method = "getMediaEncrypt" 1266 | t.Data.Type.Name = []string{"media_encrypt"} 1267 | return t 1268 | } 1269 | 1270 | func connectionTypeTemplate(values ...any) connectionType { 1271 | t := connectionType{} 1272 | t.Method = "getConnectionType" 1273 | t.Data.Type.Name = []string{"get_connection_type"} 1274 | return t 1275 | } 1276 | 1277 | func lightFrequencyInfoTemplate(values ...any) lightFrequencyInfo { 1278 | t := lightFrequencyInfo{} 1279 | t.Method = "getLightFrequencyInfo" 1280 | t.Data.Type.Name = "common" 1281 | return t 1282 | } 1283 | 1284 | func lightFrequencyCapabilityTemplate(values ...any) lightFrequencyCapability { 1285 | t := lightFrequencyCapability{} 1286 | t.Method = "getLightFrequencyCapability" 1287 | t.Data.Type.Name = "common" 1288 | return t 1289 | } 1290 | 1291 | func childDeviceListTemplate(values ...any) childDeviceList { 1292 | t := childDeviceList{} 1293 | t.Method = "getChildDeviceList" 1294 | t.Data.Type.StartIndex = 0 1295 | return t 1296 | } 1297 | 1298 | func rotationStatusTemplate(values ...any) rotationStatus { 1299 | t := rotationStatus{} 1300 | t.Method = "getRotationStatus" 1301 | t.Data.Type.Name = []string{"switch"} 1302 | return t 1303 | } 1304 | 1305 | func nightVisionModeConfigTemplate(values ...any) nightVisionModeConfig { 1306 | t := nightVisionModeConfig{} 1307 | t.Method = "getNightVisionModeConfig" 1308 | t.Data.Type.Name = "switch" 1309 | return t 1310 | } 1311 | 1312 | func whitelampStatusTemplate(values ...any) whitelampStatus { 1313 | t := whitelampStatus{} 1314 | t.Method = "getWhitelampStatus" 1315 | t.Data.Type.GetWtlStatus = []string{"null"} 1316 | return t 1317 | } 1318 | 1319 | func whitelampConfigTemplate(values ...any) whitelampConfig { 1320 | t := whitelampConfig{} 1321 | t.Method = "getWhitelampConfig" 1322 | t.Data.Type.Name = "switch" 1323 | return t 1324 | } 1325 | 1326 | func msgPushConfigTemplate(values ...any) msgPushConfig { 1327 | t := msgPushConfig{} 1328 | t.Method = "getMsgPushConfig" 1329 | t.Data.Type.Name = []string{"chn1_msg_push_info"} 1330 | return t 1331 | } 1332 | 1333 | func sdCardStatusTemplate(values ...any) sdCardStatus { 1334 | t := sdCardStatus{} 1335 | t.Method = "getSdCardStatus" 1336 | t.Data.Type.Table = []string{"hd_info"} 1337 | return t 1338 | } 1339 | 1340 | func circularRecordingConfigTemplate(values ...any) circularRecordingConfig { 1341 | t := circularRecordingConfig{} 1342 | t.Method = "getCircularRecordingConfig" 1343 | t.Data.Type.Name = "harddisk" 1344 | return t 1345 | } 1346 | 1347 | func recordPlanTemplate(values ...any) recordPlan { 1348 | t := recordPlan{} 1349 | t.Method = "getRecordPlan" 1350 | t.Data.Type.Name = []string{"chn1_channel"} 1351 | return t 1352 | } 1353 | 1354 | func firmwareAutoUpgradeConfigTemplate(values ...any) firmwareAutoUpgradeConfig { 1355 | t := firmwareAutoUpgradeConfig{} 1356 | t.Method = "getFirmwareAutoUpgradeConfig" 1357 | t.Data.Type.Name = []string{"common"} 1358 | return t 1359 | } 1360 | 1361 | // Connect is general function for connecting to Camera 1362 | func Connect(host string, user string, password string) *Tapo { 1363 | o := new(Tapo) 1364 | o.LastFile, _ = os.Getwd() 1365 | o.Host = host 1366 | o.Port = "443" 1367 | o.User = user 1368 | o.Password = password 1369 | o.init() 1370 | o.auth() 1371 | o.getDevice() 1372 | o.getImageSettings() 1373 | o.getPresets() 1374 | 1375 | return o 1376 | } 1377 | 1378 | // Firsty initialise 1379 | func (o *Tapo) init() { 1380 | o.hashedPasswordMD5 = hashNHexOld(o.Password) 1381 | o.hashedPasswordSha256 = hashNHex(o.Password) 1382 | o.hostURL = `https://` + o.Host + `:` + o.Port 1383 | 1384 | newParam := make(map[string]string) 1385 | o.Parameters = newParam 1386 | o.Parameters["Host"] = o.Host 1387 | o.Parameters["Referer"] = "https://" + o.Host + ":" + o.Port 1388 | o.Parameters["Accept"] = "application/json" 1389 | o.Parameters["Accept-Encoding"] = "gzip, deflate" 1390 | o.Parameters["User-Agent"] = "Tapo CameraClient Android" 1391 | o.Parameters["Connection"] = "close" 1392 | o.Parameters["requestByApp"] = "true" 1393 | o.Parameters["Content-Type"] = "application/json; charset=UTF-8" 1394 | 1395 | o.Settings = new(settings) 1396 | o.Elements = new(elements) 1397 | 1398 | o.Settings.VisibleOsdTime = new(child) 1399 | o.Settings.VisibleOsdTime.Value = true 1400 | o.Settings.VisibleOsdTime.run = o.setOsdTime 1401 | 1402 | o.Settings.VisibleOsdText = new(child) 1403 | o.Settings.VisibleOsdText.Value = false 1404 | o.Settings.VisibleOsdText.run = o.setOsdText 1405 | 1406 | o.Settings.OsdText = "" 1407 | 1408 | o.Elements.PrivacyMode = new(child) 1409 | o.Elements.PrivacyMode.Value = false 1410 | o.Elements.PrivacyMode.run = o.setPrivacy 1411 | 1412 | o.Elements.NightModeAuto = new(child) 1413 | o.Elements.NightModeAuto.Value = true 1414 | o.Elements.NightModeAuto.run = o.setNightModeAuto 1415 | 1416 | o.Elements.NightMode = new(child) 1417 | o.Elements.NightMode.Value = true 1418 | o.Elements.NightMode.run = o.setNightMode 1419 | 1420 | o.Elements.Indicator = new(child) 1421 | o.Elements.Indicator.Value = true 1422 | o.Elements.Indicator.run = o.setLed 1423 | 1424 | o.Elements.AutotrackingMode = new(child) 1425 | o.Elements.AutotrackingMode.Value = false 1426 | o.Elements.AutotrackingMode.run = o.setAutotracking 1427 | 1428 | o.Settings.PresetChangeOsd = new(child) 1429 | o.Settings.PresetChangeOsd.Value = false 1430 | o.Settings.PresetChangeOsd.run = fnil 1431 | 1432 | o.Elements.DetectModeUpdateSens = new(child) 1433 | o.Elements.DetectModeUpdateSens.Value = false 1434 | o.Elements.DetectModeUpdateSens.run = o.updateSens 1435 | 1436 | o.Elements.DetectMode = new(child) 1437 | o.Elements.DetectMode.Value = false 1438 | o.Elements.DetectMode.run = o.setDetect 1439 | 1440 | o.Elements.DetectPersonMode = new(child) 1441 | o.Elements.DetectPersonMode.Value = false 1442 | o.Elements.DetectPersonMode.run = o.setDetectPerson 1443 | 1444 | o.Settings.DetectSensitivity = 1 1445 | 1446 | o.Settings.DetectSoundAlternativeMode = new(child) 1447 | o.Settings.DetectSoundAlternativeMode.Value = false 1448 | o.Settings.DetectSoundAlternativeMode.run = fnil 1449 | 1450 | o.Settings.DetectEnableSound = new(child) 1451 | o.Settings.DetectEnableSound.Value = true 1452 | o.Settings.DetectEnableSound.run = fnil 1453 | 1454 | o.Settings.DetectEnableFlash = new(child) 1455 | o.Settings.DetectEnableFlash.Value = false 1456 | o.Settings.DetectEnableFlash.run = fnil 1457 | 1458 | o.Elements.AlarmMode = new(child) 1459 | o.Elements.AlarmMode.Value = false 1460 | o.Elements.AlarmMode.run = o.setAlarm 1461 | 1462 | o.Elements.AlarmModeUpdateFlash = new(child) 1463 | o.Elements.AlarmModeUpdateFlash.Value = false 1464 | o.Elements.AlarmModeUpdateFlash.run = o.updateAlarmFlash 1465 | 1466 | o.Elements.AlarmModeUpdateSound = new(child) 1467 | o.Elements.AlarmModeUpdateSound.Value = false 1468 | o.Elements.AlarmModeUpdateSound.run = o.updateAlarmSound 1469 | 1470 | o.Settings.Time = new(child) 1471 | o.Settings.Time.Value = true 1472 | o.Settings.Time.run = o.getTime 1473 | 1474 | o.Settings.PrintImageSettings = new(child) 1475 | o.Settings.PrintImageSettings.Value = true 1476 | o.Settings.PrintImageSettings.run = o.getImageSettings 1477 | 1478 | o.Elements.ImageCorrection = new(child) 1479 | o.Elements.ImageCorrection.Value = true 1480 | o.Elements.ImageCorrection.run = o.setImageCorrection 1481 | 1482 | o.Elements.ImageFlip = new(child) 1483 | o.Elements.ImageFlip.Value = true 1484 | o.Elements.ImageFlip.run = o.setImageFlip 1485 | 1486 | o.Elements.MoveX = "" 1487 | o.Elements.MoveY = "" 1488 | o.Settings.Move = new(child) 1489 | o.Settings.Move.Value = true 1490 | o.Settings.Move.run = o.setMoveAction 1491 | 1492 | o.NextPreset = o.setNextPreset 1493 | o.Reboot = o.rebootDevice 1494 | } 1495 | 1496 | // Pack and encode request 1497 | func pack(request any, key, iv []byte) secure { 1498 | requestJSON, err := json.Marshal(request) 1499 | if err != nil { 1500 | p("Error pack query") 1501 | } 1502 | return secureTemplate( 1503 | encodeB64( 1504 | encodeAES(requestJSON, key, iv), 1505 | ), 1506 | ) 1507 | //return template[secure](encodeB64(encodeAES(requestJSON, key, iv))) 1508 | } 1509 | 1510 | // POST query to cam 1511 | func (o *Tapo) query(data any, host string, encrypt bool) []byte { 1512 | if encrypt { 1513 | data = pack(data, o.Key, o.Iv) 1514 | } 1515 | dataBody, _ := json.Marshal(data) 1516 | body := bytes.NewReader(dataBody) 1517 | req, _ := http.NewRequest("POST", host, body) 1518 | for k, v := range o.Parameters { 1519 | req.Header.Add(k, v) 1520 | } 1521 | if encrypt { 1522 | req.Header.Add("Seq", o.Seq) 1523 | req.Header.Add("Tapo_tag", hashNHex(hashNHex(o.hashedPassword)+string(dataBody)+o.Seq)) 1524 | } 1525 | tr := &http.Transport{ 1526 | TLSClientConfig: &tls.Config{ 1527 | InsecureSkipVerify: true, 1528 | }, 1529 | } 1530 | client := &http.Client{Transport: tr} 1531 | resp, err := client.Do(req) 1532 | if err != nil { 1533 | return []byte{} 1534 | } 1535 | b, err := io.ReadAll(resp.Body) 1536 | if err != nil { 1537 | return []byte{} 1538 | } 1539 | if encrypt { 1540 | result := new(queryResponse) 1541 | json.Unmarshal(b, &result) 1542 | return []byte(decodeAES([]byte(decodeB64(result.Result.Response)), o.Key, o.Iv)) 1543 | } 1544 | defer resp.Body.Close() 1545 | return b 1546 | } 1547 | 1548 | // Check insecure of authorise. 1549 | // At this moment the simplest way is send hash(sha256) 1550 | // (but not best and stable). 1551 | // On firmware >= 1.3.9(11 for new hardware) old type of authorise with hash(md5) will not valid 1552 | func (o *Tapo) auth() { 1553 | o.hashedPassword = o.hashedPasswordSha256 1554 | o.Encrypt = false 1555 | result := new(updateStokReturn) 1556 | json.Unmarshal(o.query(updateStokTemplate(o.User, o.hashedPassword), o.hostURL, false), &result) 1557 | if result.ErrorCode == 0 && result.Result.StartSeq != nil { 1558 | o.InsecureAuth = true 1559 | } else { 1560 | o.InsecureAuth = false 1561 | } 1562 | } 1563 | 1564 | func hash(value string) []byte { 1565 | h := sha256.Sum256([]byte(value)) 1566 | return h[:] 1567 | } 1568 | 1569 | func hashNHex(value string) string { 1570 | return strings.ToUpper(fmt.Sprintf("%x", sha256.Sum256([]byte(value)))) 1571 | } 1572 | 1573 | func hashNHexOld(value string) string { 1574 | return strings.ToUpper(fmt.Sprintf("%x", md5.Sum([]byte(value)))) 1575 | } 1576 | 1577 | func encodeAES(text []byte, key []byte, iv []byte) string { 1578 | pad := aes.BlockSize - len(text)%aes.BlockSize 1579 | bText := append(text, bytes.Repeat([]byte{byte(pad)}, pad)...) 1580 | block, err := aes.NewCipher(key) 1581 | if err != nil { 1582 | p(err) 1583 | } 1584 | encoded := make([]byte, len(bText)) 1585 | cipher.NewCBCEncrypter(block, iv).CryptBlocks(encoded, bText) 1586 | return string(encoded) 1587 | } 1588 | 1589 | func decodeAES(text []byte, key []byte, iv []byte) string { 1590 | decoded := text 1591 | block, err := aes.NewCipher(key) 1592 | if err != nil { 1593 | p(err) 1594 | } 1595 | cipher.NewCBCDecrypter(block, iv).CryptBlocks(decoded, decoded) 1596 | return string(decoded) 1597 | } 1598 | 1599 | func encodeB64(text string) string { 1600 | return base64.StdEncoding.EncodeToString([]byte(text)) 1601 | } 1602 | 1603 | func decodeB64(text string) string { 1604 | data, err := base64.StdEncoding.DecodeString(text) 1605 | if err != nil { 1606 | return "" 1607 | } 1608 | return string(data) 1609 | } 1610 | 1611 | func (o *Tapo) getDigestPasswd() (string, string) { 1612 | result := new(loginInsecureResponse) 1613 | err := json.Unmarshal(o.query(loginInitTemplate(o.User), o.hostURL, false), &result) 1614 | if err != nil { 1615 | p("Check! response struct outdated") 1616 | } 1617 | return hashNHex(o.hashedPassword + result.Result.Data.Nonce), result.Result.Data.Nonce 1618 | } 1619 | 1620 | // Refresh stok. For authentication 1621 | func (o *Tapo) update() { 1622 | if o.InsecureAuth { 1623 | o.updateInsecure() 1624 | } else { 1625 | o.updateRaw() 1626 | } 1627 | } 1628 | 1629 | func (o *Tapo) updateInsecure() { 1630 | o.hashedPassword = o.hashedPasswordSha256 1631 | hashPass, nonce := o.getDigestPasswd() 1632 | o.Key = hash("lsk" + nonce + hashPass)[:aes.BlockSize] 1633 | o.Iv = hash("ivb" + nonce + hashPass)[:aes.BlockSize] 1634 | o.Encrypt = true 1635 | result := new(updateStokReturn) 1636 | json.Unmarshal(o.query(loginNewTemplate(o.User, hashPass+nonce), o.hostURL, false), &result) 1637 | if result.ErrorCode == 0 && result.Result.StartSeq != nil { 1638 | //version >= 1.3.9(11) 1639 | o.stokID = result.Result.Stok 1640 | o.hostURLStok = o.hostURL + `/stok=` + o.stokID + `/ds` 1641 | o.Seq = strconv.Itoa(*result.Result.StartSeq) 1642 | o.userGroup = result.Result.UserGroup 1643 | } else { 1644 | p(`Authenticate failed. Try use another cred.`) 1645 | } 1646 | } 1647 | 1648 | func (o *Tapo) updateRaw() { 1649 | o.hashedPassword = o.hashedPasswordMD5 1650 | o.Encrypt = false 1651 | result := new(updateStokReturn) 1652 | json.Unmarshal(o.query(updateStokTemplate(o.User, o.hashedPassword), o.hostURL, false), &result) 1653 | if result.ErrorCode == 0 && result.Result.StartSeq == nil { 1654 | //version < 1.3.9(11) 1655 | o.stokID = result.Result.Stok 1656 | o.hostURLStok = o.hostURL + `/stok=` + o.stokID + `/ds` 1657 | o.userGroup = result.Result.UserGroup 1658 | } else { 1659 | p(`Authenticate failed. Try use another cred. login - "admin", password - your password in Tapo account.`) 1660 | if o.UserDef != o.User { 1661 | o.UserDef = o.User 1662 | o.User = "admin" 1663 | p(`App will be trying with "admin" with next operation(if your pass on rtsp equal pass your account). If next operation exist.`) 1664 | } else { 1665 | p(`Authenticate failed. Login by "admin" has failed.`) 1666 | panic("!end operation authenticate!") 1667 | } 1668 | } 1669 | } 1670 | 1671 | // Get information about device tapo c200 1672 | func (o *Tapo) getDevice() { 1673 | o.update() 1674 | ret := o.query(manyTemplate(deviceInfoTemplate("")), o.hostURLStok, o.Encrypt) 1675 | result := new(deviceRet) 1676 | json.NewDecoder(bytes.NewReader(ret)).Decode(&result) 1677 | o.deviceID = result.Result.Responses[0].Result.DeviceInfo.BasicInfo.DevID 1678 | o.deviceModel = result.Result.Responses[0].Result.DeviceInfo.BasicInfo.DeviceModel 1679 | } 1680 | 1681 | // Manual move 1682 | // 1683 | // 10 = 10 degree 1684 | // 1685 | // -10 = 10 degree reverse 1686 | func (o *Tapo) setMovePosition(x, y int) { 1687 | o.update() 1688 | o.query(movePositionTemplate(x, y), o.hostURLStok, o.Encrypt) 1689 | } 1690 | 1691 | // Move action by X and Y 1692 | func (o *Tapo) setMoveAction() { 1693 | x, _ := strconv.Atoi(o.Elements.MoveX) 1694 | y, _ := strconv.Atoi(o.Elements.MoveY) 1695 | o.setMovePosition(x, y) 1696 | } 1697 | 1698 | // Get all making Presets in App 1699 | func (o *Tapo) getPresets() { 1700 | o.update() 1701 | ret := o.query(manyTemplate(presetConfigTemplate("")), o.hostURLStok, o.Encrypt) 1702 | result := new(presetListReturn) 1703 | json.NewDecoder(bytes.NewReader(ret)).Decode(&result) 1704 | if len(result.Result.Responses) > 0 { 1705 | o.Rotate = true 1706 | } 1707 | for _, v := range result.Result.Responses { 1708 | for kk, vv := range v.Result.Preset.Preset.ID { 1709 | o.presets = append(o.presets, &presets{ID: vv, Name: v.Result.Preset.Preset.Name[kk]}) 1710 | } 1711 | } 1712 | } 1713 | 1714 | // Switch to next preset 1715 | func (o *Tapo) setNextPreset() { 1716 | if o.Rotate { 1717 | o.update() 1718 | o.lastPosition = o.rLast() 1719 | if len(o.presets) > o.lastPosition+1 { 1720 | o.lastPosition++ 1721 | } else { 1722 | o.lastPosition = 0 1723 | } 1724 | next := o.presets[o.lastPosition] 1725 | if o.Settings.PresetChangeOsd.Value { 1726 | o.Settings.OsdText = next.Name 1727 | o.Settings.VisibleOsdText.Value = true 1728 | o.Settings.VisibleOsdTime.Value = true 1729 | o.setOsdText() 1730 | } 1731 | o.query( 1732 | nextPresetTemplate(next.ID), 1733 | o.hostURLStok, 1734 | o.Encrypt, 1735 | ) 1736 | o.wLast(o.lastPosition) 1737 | } 1738 | } 1739 | 1740 | // Write log last file 1741 | func (o *Tapo) wLast(v int) { 1742 | text := strconv.Itoa(v) 1743 | os.WriteFile(filepath.Join(o.LastFile, o.deviceID+".last_preset"), []byte(text), 0775) 1744 | } 1745 | 1746 | // Read log last file 1747 | func (o *Tapo) rLast() int { 1748 | if lastDef, err := os.ReadFile(filepath.Join(o.LastFile, o.deviceID+".last_preset")); err == nil { 1749 | last, _ := strconv.Atoi(string(lastDef)) 1750 | return last 1751 | } 1752 | os.Create(filepath.Join(o.LastFile, o.deviceID+".last_preset")) 1753 | return 0 1754 | } 1755 | 1756 | // Run all presets with timer beetween 1757 | func (o *Tapo) runAllPresets(timer string) { 1758 | if o.Rotate { 1759 | durDef, _ := time.ParseDuration(timer) 1760 | for range o.presets { 1761 | time.Sleep(durDef) 1762 | o.setNextPreset() 1763 | } 1764 | } 1765 | } 1766 | 1767 | // Reboot device 1768 | func (o *Tapo) rebootDevice() { 1769 | o.update() 1770 | o.query(rebootTemplate(), o.hostURLStok, o.Encrypt) 1771 | } 1772 | 1773 | // special function xBool 1774 | func (o *Types) xBool(s interface{}) *Types { 1775 | switch val := s.(type) { 1776 | case string: 1777 | if _, err := strconv.Atoi(val); err == nil { 1778 | ss := strings.Split(val, "") 1779 | sslen := strconv.Itoa(len(ss)) 1780 | ssss := ss[len(ss)-1] 1781 | tmp, _ := reflect.TypeOf(*new(Stages)).FieldByName("sBool" + sslen) 1782 | if val, ex := tmp.Tag.Lookup(ssss); ex { 1783 | o.Default = val 1784 | } 1785 | } else { 1786 | o.Default = val 1787 | o.Head = o.Default 1788 | o.test() 1789 | return o 1790 | } 1791 | case bool: 1792 | switch val { 1793 | case true: 1794 | tmp, _ := reflect.TypeOf(*new(Stages)).FieldByName("sBool" + DefXBool) 1795 | if val, ex := tmp.Tag.Lookup("1"); ex { 1796 | o.Default = val 1797 | } 1798 | case false: 1799 | tmp, _ := reflect.TypeOf(*new(Stages)).FieldByName("sBool" + DefXBool) 1800 | if val, ex := tmp.Tag.Lookup("0"); ex { 1801 | o.Default = val 1802 | } 1803 | } 1804 | case int: 1805 | ss := strings.Split(strconv.Itoa(val), "") 1806 | sslen := strconv.Itoa(len(ss)) 1807 | ssss := ss[len(ss)-1] 1808 | tmp, _ := reflect.TypeOf(*new(Stages)).FieldByName("sBool" + sslen) 1809 | if val, ex := tmp.Tag.Lookup(ssss); ex { 1810 | o.Default = val 1811 | } 1812 | } 1813 | o.Head = strings.Split(o.Default, "")[0] 1814 | o.test() 1815 | return o 1816 | } 1817 | 1818 | // special function xBool 1819 | func (o *Types) test() { 1820 | tmp, _ := reflect.TypeOf(*new(Stages)).FieldByName("True") 1821 | if val, ex := tmp.Tag.Lookup(o.Head); ex { 1822 | b, _ := strconv.ParseBool(val) 1823 | o.isTrue = b 1824 | } 1825 | tmp, _ = reflect.TypeOf(*new(Stages)).FieldByName("False") 1826 | if val, ex := tmp.Tag.Lookup(o.Head); ex { 1827 | b, _ := strconv.ParseBool(val) 1828 | o.isFalse = b 1829 | } 1830 | tmp, _ = reflect.TypeOf(*new(Stages)).FieldByName("Binary") 1831 | if val, ex := tmp.Tag.Lookup(o.Head); ex { 1832 | b, _ := strconv.ParseBool(val) 1833 | o.isBinary = b 1834 | } 1835 | tmp, _ = reflect.TypeOf(*new(Stages)).FieldByName("Command") 1836 | if val, ex := tmp.Tag.Lookup(o.Head); ex { 1837 | b, _ := strconv.ParseBool(val) 1838 | o.isCommand = b 1839 | } 1840 | tmp, _ = reflect.TypeOf(*new(Stages)).FieldByName("isSpecial") 1841 | if val, ex := tmp.Tag.Lookup(o.Head); ex { 1842 | o.isSpecial = val 1843 | } 1844 | tmp, _ = reflect.TypeOf(*new(Stages)).FieldByName("Next") 1845 | if val, ex := tmp.Tag.Lookup(o.Head); ex { 1846 | o.Next = val 1847 | } 1848 | } 1849 | 1850 | func sBool(value interface{}) bool { 1851 | switch val := value.(type) { 1852 | case string: 1853 | switch val { 1854 | case "+": 1855 | return true 1856 | case "-": 1857 | return false 1858 | case "true": 1859 | return true 1860 | case "false": 1861 | return false 1862 | case "🔴": 1863 | return false 1864 | case "🟢": 1865 | return true 1866 | case "0": 1867 | return false 1868 | case "1": 1869 | return true 1870 | case "off": 1871 | return false 1872 | case "on": 1873 | return true 1874 | } 1875 | case bool: 1876 | return val 1877 | default: 1878 | fmt.Println("Error!") 1879 | return false 1880 | } 1881 | return false 1882 | } 1883 | 1884 | func (o *Tapo) getAlarm() (string, []string, string) { 1885 | o.update() 1886 | result := new(lastAlarmInfoResponse) 1887 | json.NewDecoder(bytes.NewReader(o.query(manyTemplate(lastAlarmInfoTemplate("")), o.hostURLStok, o.Encrypt))).Decode(&result) 1888 | return result.Result.Responses[0].Result.MsgAlarm.Chn1MsgAlarmInfo.Enabled, result.Result.Responses[0].Result.MsgAlarm.Chn1MsgAlarmInfo.AlarmMode, result.Result.Responses[0].Result.MsgAlarm.Chn1MsgAlarmInfo.AlarmType 1889 | } 1890 | 1891 | func (o *Tapo) updateAlarmSound() { 1892 | o.update() 1893 | enabled, list, alarmType := o.getAlarm() 1894 | newList := []string{} 1895 | for _, v := range list { 1896 | if v != "sound" && enabled == "on" { 1897 | newList = append(newList, v) 1898 | } 1899 | } 1900 | 1901 | if o.Elements.AlarmModeUpdateSound.Value { 1902 | newList = append(newList, "sound") 1903 | enabled = "on" 1904 | } 1905 | 1906 | if len(newList) == 0 { 1907 | enabled = "off" 1908 | newList = []string{"sound", "light"} 1909 | } 1910 | 1911 | o.query( 1912 | alarmTemplate( 1913 | alarmType, 1914 | newList, 1915 | enabled, 1916 | ), 1917 | o.hostURLStok, 1918 | o.Encrypt, 1919 | ) 1920 | } 1921 | 1922 | func (o *Tapo) updateAlarmFlash() { 1923 | o.update() 1924 | enabled, list, alarmType := o.getAlarm() 1925 | newList := []string{} 1926 | for _, v := range list { 1927 | if v != "light" && enabled == "on" { 1928 | newList = append(newList, v) 1929 | } 1930 | } 1931 | 1932 | if o.Elements.AlarmModeUpdateFlash.Value { 1933 | newList = append(newList, "light") 1934 | enabled = "on" 1935 | } 1936 | 1937 | if len(newList) == 0 { 1938 | enabled = "off" 1939 | newList = []string{"sound", "light"} 1940 | } 1941 | 1942 | o.query( 1943 | alarmTemplate( 1944 | alarmType, 1945 | newList, 1946 | enabled, 1947 | ), 1948 | o.hostURLStok, 1949 | o.Encrypt, 1950 | ) 1951 | } 1952 | 1953 | // Set alarm mode 1954 | // DetectEnableSound - include noise 1955 | // DetectSoundAlternativeMode - sound like a bip 1956 | // DetectEnableFlash - blinking led diode 1957 | func (o *Tapo) setAlarm() { 1958 | o.update() 1959 | 1960 | list := []string{} 1961 | 1962 | if o.Settings.DetectEnableSound.Value && o.Settings.DetectEnableFlash.Value { 1963 | list = []string{"sound", "light"} 1964 | } else if !o.Settings.DetectEnableSound.Value && !o.Settings.DetectEnableFlash.Value { 1965 | list = []string{"sound", "light"} 1966 | } else if o.Settings.DetectEnableSound.Value { 1967 | list = []string{"sound"} 1968 | } else if o.Settings.DetectEnableFlash.Value { 1969 | list = []string{"light"} 1970 | } 1971 | 1972 | alarmType := "0" 1973 | if o.Settings.DetectSoundAlternativeMode.Value { 1974 | alarmType = "1" 1975 | } 1976 | 1977 | o.query( 1978 | alarmTemplate( 1979 | alarmType, 1980 | list, 1981 | new(Types).xBool(o.Elements.AlarmMode.Value).Default, 1982 | ), 1983 | o.hostURLStok, 1984 | o.Encrypt, 1985 | ) 1986 | } 1987 | 1988 | // Turn Indicator diode (red, green) 1989 | func (o *Tapo) setLedAction(value string) { //on off 1990 | o.update() 1991 | o.query(setLedTemplate(value), o.hostURLStok, o.Encrypt) 1992 | } 1993 | 1994 | // Turn Indicator diode (red, green) 1995 | func (o *Tapo) setLed() { 1996 | o.setLedAction(new(Types).xBool(o.Elements.Indicator.Value).Default) 1997 | } 1998 | 1999 | // Get Time 2000 | func (o *Tapo) getTime() { 2001 | o.update() 2002 | result := new(getTimeRet) 2003 | json.Unmarshal(o.query(getTimeTemplate(), o.hostURLStok, o.Encrypt), &result) 2004 | o.TimeStr = result.System.ClockStatus.LocalTime 2005 | } 2006 | 2007 | // Get Settings Image 2008 | func (o *Tapo) getImageSettings() { 2009 | o.update() 2010 | result := new(getImageSettingsRet) 2011 | json.NewDecoder(bytes.NewReader(o.query(manyTemplate(ldcTemplate()), o.hostURLStok, o.Encrypt))).Decode(&result) 2012 | o.FishEye = new(Types).xBool(result.Result.Responses[0].Result.Image.Switch.Ldc).isTrue 2013 | o.Flip = result.Result.Responses[0].Result.Image.Switch.FlipType == "center" 2014 | } 2015 | 2016 | // Set Correction 2017 | func (o *Tapo) setImageCorrection() { 2018 | o.update() 2019 | o.query(setImageCorrectionTemplate(new(Types).xBool(o.Elements.ImageCorrection.Value).Default), o.hostURLStok, o.Encrypt) 2020 | } 2021 | 2022 | // Set Flip 2023 | func (o *Tapo) setImageFlip() { 2024 | val := new(Types).xBool(o.Elements.ImageFlip.Value).Default 2025 | if o.Elements.ImageFlip.Value { 2026 | val = "center" 2027 | } 2028 | o.update() 2029 | o.query(setImageFlipTemplate(val), o.hostURLStok, o.Encrypt) 2030 | } 2031 | 2032 | // Motion detect with sensitivity 2033 | func (o *Tapo) getDetect() string { 2034 | o.update() 2035 | result := new(detectionConfigResponse) 2036 | ret := o.query(manyTemplate(detectionConfigTemplate("")), o.hostURLStok, o.Encrypt) 2037 | json.NewDecoder(bytes.NewReader(ret)).Decode(&result) 2038 | return result.Result.Responses[0].Result.MotionDetection.MotionDet.Enabled 2039 | } 2040 | 2041 | // Motion detect with sensitivity 2042 | func (o *Tapo) updateSens() { 2043 | o.update() 2044 | enabled := o.getDetect() 2045 | o.query(detectTemplate(enabled, o.Settings.DetectSensitivity), o.hostURLStok, o.Encrypt) 2046 | } 2047 | 2048 | // Motion detect with sensitivity 2049 | func (o *Tapo) setDetect() { 2050 | o.update() 2051 | o.query(detectTemplate(new(Types).xBool(o.Elements.DetectMode.Value).Default, o.Settings.DetectSensitivity), o.hostURLStok, o.Encrypt) 2052 | } 2053 | 2054 | // Motion detect with sensitivity 2055 | func (o *Tapo) setDetectPerson() { 2056 | o.update() 2057 | o.query(setPersonDetectTemplate(new(Types).xBool(o.Elements.DetectPersonMode.Value).Default), o.hostURLStok, o.Encrypt) 2058 | } 2059 | 2060 | // Turn camera in private mode with stop video channel 2061 | func (o *Tapo) setPrivacy() { 2062 | o.update() 2063 | o.query(privacyTemplate(new(Types).xBool(o.Elements.PrivacyMode.Value).Default), o.hostURLStok, o.Encrypt) 2064 | } 2065 | 2066 | // Turn irc flashlight 2067 | func (o *Tapo) setNightMode() { 2068 | o.update() 2069 | o.query(nightModeTemplate(new(Types).xBool(o.Elements.NightMode.Value).Default), o.hostURLStok, o.Encrypt) 2070 | } 2071 | 2072 | // Turn irc flashlight 2073 | func (o *Tapo) setNightModeAuto() { 2074 | if o.Elements.NightModeAuto.Value { 2075 | o.update() 2076 | o.query(nightModeTemplate("auto"), o.hostURLStok, o.Encrypt) 2077 | } 2078 | } 2079 | 2080 | // Autotracking all motion. BETA 2081 | func (o *Tapo) setAutotracking() { 2082 | o.update() 2083 | o.query(autotrackingTemplate(new(Types).xBool(o.Elements.AutotrackingMode.Value).Default), o.hostURLStok, o.Encrypt) 2084 | } 2085 | 2086 | // get Text OSD 2087 | func (o *Tapo) getOsd() (string, string) { 2088 | o.update() 2089 | result := new(getOSDRet) 2090 | json.NewDecoder(bytes.NewReader(o.query(getOSDTemplate(), o.hostURLStok, o.Encrypt))).Decode(&result) 2091 | if len(o.Settings.OsdText) == 0 { 2092 | o.Settings.OsdText = result.OSD.LabelInfo[0].LabelInfo1.Text 2093 | } 2094 | return result.OSD.LabelInfo[0].LabelInfo1.Enabled, result.OSD.Date.Enabled 2095 | } 2096 | 2097 | // Text OSD 2098 | func (o *Tapo) setOsdTime() { 2099 | textEnabled, _ := o.getOsd() 2100 | o.update() 2101 | o.query( 2102 | osdTemplate( 2103 | new(Types).xBool(o.Settings.VisibleOsdTime.Value).Default, 2104 | textEnabled, 2105 | o.Settings.OsdText, 2106 | ), 2107 | o.hostURLStok, 2108 | o.Encrypt, 2109 | ) 2110 | } 2111 | 2112 | // Text OSD 2113 | func (o *Tapo) setOsdText() { 2114 | _, timeEnabled := o.getOsd() 2115 | 2116 | if len(o.Settings.OsdText) > 16 { 2117 | //16 symbols, not bytes 2118 | o.Settings.OsdText = string([]rune(o.Settings.OsdText)[0:16]) 2119 | } 2120 | 2121 | o.update() 2122 | o.query( 2123 | osdTemplate( 2124 | timeEnabled, 2125 | new(Types).xBool(o.Settings.VisibleOsdText.Value).Default, 2126 | o.Settings.OsdText, 2127 | ), 2128 | o.hostURLStok, 2129 | o.Encrypt, 2130 | ) 2131 | } 2132 | 2133 | // On is turn settings 2134 | func (o *child) On() { 2135 | o.Value = true 2136 | o.run() 2137 | } 2138 | 2139 | // Off is turn settings 2140 | func (o *child) Off() { 2141 | o.Value = false 2142 | o.run() 2143 | } 2144 | 2145 | // On is turn settings 2146 | func (o *Tapo) On(s Action) { 2147 | s.On() 2148 | } 2149 | 2150 | // Off is turn settings 2151 | func (o *Tapo) Off(s Action) { 2152 | s.Off() 2153 | } 2154 | 2155 | // MoveRight is moving cam to right 2156 | func (o *Tapo) MoveRight(val int) { 2157 | o.setMovePosition(val, 0) 2158 | time.Sleep(5 * time.Second) 2159 | } 2160 | 2161 | // MoveLeft is moving cam to left 2162 | func (o *Tapo) MoveLeft(val int) { 2163 | o.setMovePosition(-val, 0) 2164 | time.Sleep(5 * time.Second) 2165 | } 2166 | 2167 | // MoveUp is moving cam to up 2168 | func (o *Tapo) MoveUp(val int) { 2169 | o.setMovePosition(0, val) 2170 | time.Sleep(5 * time.Second) 2171 | } 2172 | 2173 | // MoveDown is moving cam to down 2174 | func (o *Tapo) MoveDown(val int) { 2175 | o.setMovePosition(0, -val) 2176 | time.Sleep(5 * time.Second) 2177 | } 2178 | 2179 | // MoveTest is moving cam to all presets 2180 | func (o *Tapo) MoveTest() { 2181 | o.runAllPresets("10s") 2182 | } 2183 | --------------------------------------------------------------------------------