How to use withings-go
withings-go
Go library zono-dev/withings-go is for Withings API.
Currently it support resource the below.
- Offline Authorization
- Measure - Getmeas
- Measure v2 - Getactivity
- Sleep v2 - Get
- Sleep v2 - Getsummary
Getting started
1. Create your account and register your app
Before you can use withings-go, you need to create your Withings account and register your app; if you own a Withings product, you should already have a Withings account.
Access Register your app to register your app.
Callback URL must satisfy the following conditions described in Withings API FAQ.
- Localhost and IP as callback url or redirect uri are not allowed.
- Starts with https.
- Be a valid URL.
- Not be greater than 255 characters.
So even if you use the withings API for personal use only, you will need your server and domain which start with https.
Client ID, Consumer Secret can be use after you complete filling in your information.
2. Install withings-go
Just run go get github.com/zono-dev/withings-go/withings
.
3. Create your settings file
Create .test_settings.yaml
in your working directory.
ID: "YOUR CLIENT ID HERE"
Secret: "YOUR CONSUMER SECRET HERE"
RedirectURL: "Your Redirect URL"
The following text is taken from Withings API FAQ.
The
redirect_uri
is set as parameter and its value is specified as Callback Url in your partner application (or contained if it is a list). It must at least have same domain.
The endpoint https://wbsapi.withings.com is not working
Correct URL is https://wbsapi.withings.net
The endpoint https://account.withings.com/oauth2_user/authorize is not working
Correct URL is https://account.withings.com/oauth2_user/authorize2
4. Create your Go app
Copy main.go in zono-dev/withings-go to your working directory.
Run go build -o getMeasurements
and getMeasurements
will be generated.
5. Run your app
getMeasurements
will read your .test_settings.yaml
automatically.
When first time you run getMeasurements
, you’ll get the message in your console as follows.
map[CID:YOUR-CLIENT-ID RedirectURL:YOUR-REDIRECT-URL Secret:YOUR-SECRET]
[user.activity,user.metrics,user.info]
URL to authorize:http://account.withings.com/oauth2_user/authorize2?access_type=offline&client_id=yourclientid&redirect_uri=https%3A%2F%2Fexample.com&response_type=code&scope=user.activity%2Cuser.metrics%2Cuser.info&state=state
Open url your browser and Enter your grant code here.
Grant Code:
Access the URL to authorize
URL in your browser and login withings site that will be permit to access your measurements.
If you allow that your app access to your measurements, your browser will be redirect your Redirect URL
with Grant Code
in GET parameters.
For example, Grant code is 12345678910abcdefghijklmnopqrstuvwxyz
in https://example.com/?code=12345678910abcdefghijklmnopqrstuvwxyz&state=state
.
Caution: Your grant code is available for 30 seconds, so you need to type it in your console immidiately.
Then your measurments will be displayed in your console.
Note: The time location used in sample code is JST, so you need to adjust it to your location. See the next section for customization.
6. Customize your app
Some customization points are in the sample code.
- Timezone:
jst
inmainSetup()
- Duration to get data:
t
andadayago
orlastupdate
inmainSetup()
- Kind of data: Args for
GetMeas()
,GetActivity()
,GetSleep()
,GetSleepSummary()
package main
import (
"fmt"
"math"
"os"
"time"
"github.com/zono-dev/withings-go/withings"
)
const (
tokenFile = "access_token.json"
layout = "2006-01-02"
layout2 = "2006-01-02 15:04:05"
isnotify = false
)
var (
jst *time.Location
t time.Time
adayago time.Time
lastupdate time.Time
ed string
sd string
client *(withings.Client)
settings map[string]string
)
func auth(settings map[string]string) {
var err error
client, err = withings.New(settings["CID"], settings["Secret"], settings["RedirectURL"])
if err != nil {
fmt.Println("Failed to create New client")
fmt.Println(err)
return
}
if _, err := os.Open(tokenFile); err != nil {
var e error
client.Token, e = withings.AuthorizeOffline(client.Conf)
client.Client = withings.GetClient(client.Conf, client.Token)
if e != nil {
fmt.Println("Failed to authorize offline.")
}
fmt.Println("~~ authorized. Let's check the token file!")
} else {
_, err = client.ReadToken(tokenFile)
if err != nil {
fmt.Println("Failed to read token file.")
fmt.Println(err)
return
}
}
}
func tokenFuncs() {
// Show token
client.PrintToken()
// Refresh Token if you need
_, rf, err := client.RefreshToken()
if err != nil {
fmt.Println("Failed to RefreshToken")
fmt.Println(err)
return
}
if rf {
fmt.Println("You got new token!")
client.PrintToken()
}
// Save Token if you need
err = client.SaveToken(tokenFile)
if err != nil {
fmt.Println("Failed to RefreshToken")
fmt.Println(err)
return
}
}
func mainSetup() {
jst = time.FixedZone("Asis/Tokyo", 9*60*60)
t = time.Now()
// to get sample data from 2 days ago to now
adayago = t.Add(-48 * time.Hour)
ed = t.Format(layout)
sd = adayago.Format(layout)
lastupdate = withings.OffsetBase
//lastupdate = time.Date(2020, 12, 20, 0, 0, 0, 0, time.UTC)
}
func printMeas(v withings.MeasureData, name, unit string) {
fmt.Printf("%s(Grpid:%v, Category:%v, Attrib: %v, DeviceID:%v)\n", name, v.GrpID, v.Category, v.Attrib, v.DeviceID)
fmt.Printf("%v, %.1f %s\n", v.Date.In(jst).Format(layout2), v.Value, unit)
}
func testGetmeas() {
fmt.Println("========== Getmeas[START] ========== ")
mym, err := client.GetMeas(withings.Real, adayago, t, lastupdate, 0, false, true, withings.Weight, withings.Height, withings.FatFreeMass, withings.BoneMass, withings.FatRatio, withings.FatMassWeight, withings.Temp, withings.HeartPulse, withings.Hydration)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Status: %d\n", mym.Status)
for _, v := range mym.SerializedData.Weights {
printMeas(v, "Weight", "Kg")
}
for _, v := range mym.SerializedData.FatFreeMass {
printMeas(v, "FatFreeMass", "Kg")
}
for _, v := range mym.SerializedData.FatRatios {
printMeas(v, "FatRatio", "%%")
}
for _, v := range mym.SerializedData.FatMassWeights {
printMeas(v, "FatMassWeight", "Kg")
}
for _, v := range mym.SerializedData.BoneMasses {
printMeas(v, "BoneMass", "Kg")
}
for _, v := range mym.SerializedData.UnknowVals {
printMeas(v, "UnknownVal", "N/A")
}
// Raw data should be provided from mym.Body.Measuregrps
for _, v := range mym.Body.Measuregrps {
weight := float64(v.Measures[0].Value) * math.Pow10(v.Measures[0].Unit)
fmt.Printf("Weight:%.1f Kgs\n", weight)
}
fmt.Printf("More:%d, Offset:%d\n", mym.Body.More, mym.Body.Offset)
fmt.Println("========== Getmeas[END] ========== ")
}
func testGetactivity() {
fmt.Println("========== Getactivity[START] ========== ")
act, err := client.GetActivity(sd, ed, 0, 0, withings.Steps, withings.Calories, withings.HrAverage, withings.HrMin, withings.HrMax)
if err != nil {
fmt.Println("getActivity Error.")
fmt.Println(err)
return
}
//fmt.Println(act)
for _, v := range act.Body.Activities {
fmt.Printf("Date:%s, Steps:%d, BurnedCalories: %g, HRAverage: %d, HRMinimum: %d, HRMax:%d \n", v.Date, v.Steps, v.Calories, v.HrAverage, v.HrMin, v.HrMax)
}
fmt.Println("========== Getactivity[END] ========== ")
}
func testGetsleep() {
fmt.Println("========== Getsleep[START] ========== ")
slp, err := client.GetSleep(adayago, t, withings.HrSleep, withings.RrSleep, withings.SnoringSleep)
if err != nil {
fmt.Println("getSleep Error!")
fmt.Println(err)
return
}
for _, v := range slp.Body.Series {
st := ""
switch v.State {
case int(withings.Awake):
st = "Awake"
case int(withings.LightSleep):
st = "LightSleep"
case int(withings.DeepSleep):
st = "DeepSleep"
case int(withings.REM):
st = "REM"
default:
st = "Unknown"
}
stimeUnix := time.Unix(v.Startdate, 0)
etimeUnix := time.Unix(v.Enddate, 0)
stime := (stimeUnix.In(jst)).Format(layout2)
etime := (etimeUnix.In(jst)).Format(layout2)
message := fmt.Sprintf("%s to %s: %s, Hr:%d, Rr:%d, Snoring:%d\n", stime, etime, st, v.Hr, v.Rr, v.Snoring)
fmt.Printf(message)
}
//fmt.Println(slp)
fmt.Println("========== Getsleep[END] ========== ")
}
func testGetsleepsummary() {
fmt.Println("========== Getsleepsummary[START] ========== ")
slpsum, err := client.GetSleepSummary(sd, ed, 0, withings.SSBdi, withings.SSDsd, withings.SSD2s, withings.SSD2w, withings.SSHrAvr, withings.SSHrMax, withings.SSHrMin, withings.SSLsd, withings.SSRsd, withings.SSRRAvr, withings.SSRRMax, withings.SSRRMin, withings.SSSS,
withings.SSSng, withings.SSSngEC, withings.SSWupC, withings.SSWupD)
if err != nil {
fmt.Println("getSleepSummary Error!")
fmt.Println(err)
return
}
for _, v := range slpsum.Body.Series {
stimeUnix := time.Unix(v.Startdate, 0)
etimeUnix := time.Unix(v.Enddate, 0)
stime := (stimeUnix.In(jst)).Format(layout2)
etime := (etimeUnix.In(jst)).Format(layout2)
message := fmt.Sprintf(
"%s-%s: BDI:%d, duration to deep sleep(sec):%d, duration to sleep(sec):%d, duration to wakeup(sec):%d, HrAverage:%d, Max:%d, Min:%d, WakeupCounts:%d",
stime, etime, v.Data.BreathingDisturbancesIntensity, v.Data.Deepsleepduration, v.Data.Durationtosleep, v.Data.Durationtowakeup, v.Data.HrAverage, v.Data.HrMax, v.Data.HrMin, v.Data.Wakeupcount)
fmt.Println(message)
}
//fmt.Println(slpsum)
fmt.Println("========== Getsleepsummary[END] ========== ")
}
func main() {
settings = withings.ReadSettings(".test_settings.yaml")
auth(settings)
tokenFuncs()
mainSetup()
testGetmeas()
testGetactivity()
testGetsleep()
testGetsleepsummary()
}
7. Need more documents?
withings-go document can be found in withings pkg.go.dev.
8. Need more features?
Welcome your Pull Request! Thanks.
zono-dev/withings-go: withings-go is UNOFFICIAL go client to acess Withings API easily.