// init runs before anything else, and loads the images for the LED pane func init() { images = make(map[string]util.Image) images["logo"] = util.LoadImage(util.ResolveImagePath("twitter-bird.png")) images["animated"] = util.LoadImage(util.ResolveImagePath("twitter-animated.gif")) images["error"] = util.LoadImage(util.ResolveImagePath("errorX.gif")) images["at"] = util.LoadImage(util.ResolveImagePath("at.gif")) images["tick"] = util.LoadImage(util.ResolveImagePath("tick.gif")) }
func NewWeatherPane(conn *ninja.Connection) *WeatherPane { pane := &WeatherPane{ siteModel: conn.GetServiceClient("$home/services/SiteModel"), image: util.LoadImage(util.ResolveImagePath("weather/loading.gif")), } pane.tempTimeout = time.AfterFunc(0, func() { pane.temperature = false }) if !enableWeatherPane { return pane } var err error pane.weather, err = owm.NewForecast("C") if err != nil { log.Warningf("Failed to load weather api:", err) enableWeatherPane = false } else { go pane.GetWeather() } return pane }
func (p *WeatherPane) GetWeather() { enableWeatherPane = false for { site := &model.Site{} err := p.siteModel.Call("fetch", config.MustString("siteId"), site, time.Second*5) if err == nil && (site.Longitude != nil || site.Latitude != nil) { p.site = site globalSite = site if site.TimeZoneID != nil { if timezone, err = time.LoadLocation(*site.TimeZoneID); err != nil { log.Warningf("error while setting timezone (%s): %s", *site.TimeZoneID, err) timezone, _ = time.LoadLocation("Local") } } break } log.Infof("Failed to get site, or site has no location.") time.Sleep(time.Second * 2) } for { p.weather.DailyByCoordinates( &owm.Coordinates{ Longitude: *p.site.Longitude, Latitude: *p.site.Latitude, }, 1, ) if len(p.weather.List) > 0 { filename := util.ResolveImagePath("weather/" + p.weather.List[0].Weather[0].Icon + ".png") if _, err := os.Stat(filename); os.IsNotExist(err) { enableWeatherPane = false fmt.Printf("Couldn't load image for weather: %s", filename) bugsnag.Notify(fmt.Errorf("Unknown weather icon: %s", filename), p.weather) } else { p.image = util.LoadImage(filename) enableWeatherPane = true } } time.Sleep(weatherUpdateInterval) } }
func NewOnOffPane(offImage string, onImage string, onStateChange func(bool), conn *ninja.Connection, thingType string) *OnOffPane { log := logger.GetLogger("OnOffPane") pane := &OnOffPane{ onImage: util.LoadImage(onImage), offImage: util.LoadImage(offImage), onStateChange: onStateChange, log: log, devices: make([]*ninja.ServiceClient, 0), conn: conn, } listening := make(map[string]bool) getChannelServicesContinuous(thingType, "on-off", nil, func(clients []*ninja.ServiceClient, err error) { if err != nil { log.Infof("Failed to update devices: %s", err) } else { log.Infof("Pane got %d on/off devices", len(clients)) pane.devices = clients for _, device := range clients { if _, ok := listening[device.Topic]; !ok { listening[device.Topic] = true device.OnEvent("state", func(state *bool, topicKeys map[string]string) bool { log.Debugf("Got on-off state: %t", *state) if time.Since(pane.lastTap) > 1*time.Second { pane.state = *state } return true }) } } } }) return pane }
func NewPairingCodePane(text string) *PairingCodePane { img := image.NewRGBA(image.Rect(0, 0, 16, 16)) width := O4b03b.Font.DrawString(img, 0, 0, text, color.Black) //log.Printf("Text '%s' width: %d", text, width) return &PairingCodePane{ text: text, textWidth: width, image: util.LoadImage(util.ResolveImagePath("code-underline.gif")), } }
// loadImages saves the PNG and GIF files in the images directory into the stateImages map func loadImages() { files, err := ioutil.ReadDir("./images") if err != nil { panic("Couldn't load images: " + err.Error()) } stateImages = make(map[string]util.Image) for _, f := range files { if strings.HasSuffix(f.Name(), ".gif") || strings.HasSuffix(f.Name(), ".png") { name := strings.TrimSuffix(strings.TrimSuffix(f.Name(), ".png"), ".gif") log.Infof("Found state image: " + name) stateImages[name] = util.LoadImage(util.ResolveImagePath(f.Name())) // also save names of images used as keys in images map stateImageNames = append(stateImageNames, name) } } }
func NewACPane(conn *ninja.Connection) *ACPane { pane := &ACPane{ thermostat: []*ninja.ServiceClient{}, acstat: []*ninja.ServiceClient{}, mode: "off", flash: false, targetTemp: 666, currentTemp: 666, images: map[string]util.Image{ "off": util.LoadImage(util.ResolveImagePath("fan.gif")), "fan": util.LoadImage(util.ResolveImagePath("fan-on.gif")), "cool": util.LoadImage(util.ResolveImagePath("fan-on-blue.gif")), "heat": util.LoadImage(util.ResolveImagePath("fan-on-red.gif")), }, } pane.ignoreTapTimer = time.AfterFunc(0, func() { pane.ignoringTap = false }) pane.sendTempTimer = time.AfterFunc(0, func() { for _, thermostat := range pane.thermostat { thermostat.Call("set", pane.targetTemp, nil, 0) } }) listening := make(map[string]bool) isFlash := func(s string) bool { return s == "SUSPENDED" } onState := func(protocol, event string, cb func(params *json.RawMessage)) { ui.GetChannelServicesContinuous("aircon", protocol, func(thing *model.Thing) bool { return true }, func(devices []*ninja.ServiceClient, err error) { if err != nil { log.Infof("Failed to update %s device: %s", protocol, err) } else { log.Infof("Got %d %s devices", len(devices), protocol) for _, device := range devices { log.Debugf("Checking %s device %s", protocol, device.Topic) if _, ok := listening[device.Topic]; !ok { // New device log.Infof("Got new %s device: %s", protocol, device.Topic) if protocol == "thermostat" { pane.thermostat = append(pane.thermostat, device) } if protocol == "acstat" { pane.acstat = append(pane.acstat, device) } if protocol == "demandcontrol" { // only need to query for the initial notification - listener will pick up the rest go func() { var state StateChangeNotification err := device.Call("get", nil, &state, time.Second*10) if err != nil { log.Errorf("Failed to fetch state from demand control channel: %s", err) } // spew.Dump("Got demand state", state) pane.flash = isFlash(state.State) }() } listening[device.Topic] = true device.OnEvent(event, func(params *json.RawMessage, values map[string]string) bool { cb(params) return true }) } } } }) } onState("temperature", "state", func(params *json.RawMessage) { var temp float64 err := json.Unmarshal(*params, &temp) if err != nil { log.Infof("Failed to unmarshal temp from %s error:%s", *params, err) } pane.currentTemp = int(temp) log.Infof("Got the temp %d", pane.currentTemp) }) onState("thermostat", "state", func(params *json.RawMessage) { var temp float64 err := json.Unmarshal(*params, &temp) if err != nil { log.Infof("Failed to unmarshal thermostat from %s error:%s", *params, err) } pane.targetTemp = int(temp) log.Infof("Got the thermostat %d", pane.targetTemp) }) onState("acstat", "state", func(params *json.RawMessage) { var state channels.ACState err := json.Unmarshal(*params, &state) if err != nil { log.Infof("Failed to unmarshal acstat from %s error:%s", *params, err) } pane.mode = *state.Mode log.Infof("Got the ac mode %d", pane.mode) }) onState("demandcontrol", "state", func(params *json.RawMessage) { // spew.Dump("demand/controlstate", params) var state StateChangeNotification err := json.Unmarshal(*params, &state) if err != nil { log.Infof("Failed to unmarshal demandstat state from %s error:%s", *params, err) } pane.flash = isFlash(state.State) log.Infof("Got the demandcontrol state %d", pane.mode) }) go ui.StartSearchTasks(conn) return pane }
func NewImagePane(image string) *ImagePane { return &ImagePane{ image: util.LoadImage(image), } }
"image/draw" "io/ioutil" "strings" "time" "fmt" "github.com/ninjasphere/gestic-tools/go-gestic-sdk" "github.com/ninjasphere/sphere-go-led-controller/fonts/O4b03b" "github.com/ninjasphere/sphere-go-led-controller/util" ) var tapInterval = time.Millisecond * 500 var introDuration = time.Millisecond * 1500 // load a particular image - for a 'logo' in this case var imageLogo = util.LoadImage(util.ResolveImagePath("logo.png")) var border = util.LoadImage(util.ResolveImagePath("border-green.gif")) var stateImages map[string]util.Image var stateImageNames []string // loadImages saves the PNG and GIF files in the images directory into the stateImages map func loadImages() { files, err := ioutil.ReadDir("./images") if err != nil { panic("Couldn't load images: " + err.Error()) } stateImages = make(map[string]util.Image)
func NewMediaPane(conn *ninja.Connection) *MediaPane { log := logger.GetLogger("MediaPane") pane := &MediaPane{ log: log, conn: conn, gestureSync: &sync.Mutex{}, volumeImage: util.LoadImage(mediaImages.Volume), volumeUpImage: util.LoadImage(mediaImages.VolumeUp), volumeDownImage: util.LoadImage(mediaImages.VolumeDown), muteImage: util.LoadImage(mediaImages.Mute), playImage: util.LoadImage(mediaImages.Play), pauseImage: util.LoadImage(mediaImages.Pause), stopImage: util.LoadImage(mediaImages.Stop), nextImage: util.LoadImage(mediaImages.Next), playingState: "stopped", lastVolumeTime: time.Now(), //lastAirWheelTime: time.Now(), } e := func(state string) func(params *json.RawMessage, values map[string]string) bool { return func(params *json.RawMessage, values map[string]string) bool { if !pane.ignoringTap { pane.log.Infof("Received control event. Setting playing state to %s", state) pane.playingState = state } return true } } listening := make(map[string]bool) getChannelServicesContinuous("mediaplayer", "media-control", nil, func(devices []*ninja.ServiceClient, err error) { if err != nil { log.Infof("Failed to update control devices: %s", err) } else { pane.controlDevices = devices log.Infof("Got %d media-control devices", len(devices)) for _, device := range devices { if _, ok := listening[device.Topic]; !ok { listening[device.Topic] = true // New Device log.Infof("Got new control device: %s", device.Topic) device.OnEvent("playing", e("playing")) device.OnEvent("buffering", e("playing")) device.OnEvent("paused", e("paused")) device.OnEvent("stopped", e("stopped")) } } } if len(pane.controlDevices) == 0 { pane.volumeMode = true pane.log.Debugf("No control devices. Forcing volume mode.") } }) getChannelServicesContinuous("mediaplayer", "volume", nil, func(devices []*ninja.ServiceClient, err error) { if err != nil { log.Infof("Failed to update volume devices: %s", err) } else { log.Infof("Got %d volume devices", len(devices)) pane.volumeDevices = devices pane.volumeUpDownMode = false for _, device := range devices { if !isValueInList("set", device.SupportedMethods) && isValueInList("volumeUp", device.SupportedMethods) { pane.log.Infof("Volume up/down mode!") pane.volumeUpDownMode = true } } for _, device := range devices { log.Debugf("Checking volume device %s", device.Topic) if _, ok := listening[device.Topic]; !ok { listening[device.Topic] = true // New device pane.log.Infof("Got new volume device: %s supported: %v", device.Topic, device.SupportedMethods) device.OnEvent("state", func(params *json.RawMessage, values map[string]string) bool { if time.Since(pane.lastVolumeTime) > time.Millisecond*500 { var volume channels.VolumeState err := json.Unmarshal(*params, &volume) if err != nil { pane.log.Infof("Failed to unmarshal volume from %s error:%s", *params, err) } pane.volume = *volume.Level } return true }) } } } if len(pane.controlDevices) == 0 { pane.volumeMode = true pane.log.Debugf("No control devices. Forcing volume mode.") } }) pane.volumeModeReset = time.AfterFunc(0, func() { if len(pane.controlDevices) > 0 { pane.volumeMode = false pane.log.Debugf("Going back to volume mode") } }) pane.ignoreTapTimer = time.AfterFunc(0, func() { pane.ignoringTap = false }) pane.volumeUpDownTimer = time.AfterFunc(0, func() { pane.volumeUpDown = nil }) return pane }
func NewPairingColorPane(maskImage string, color color.Color) *PairingColorPane { return &PairingColorPane{ image: &util.MaskImage{util.LoadImage(maskImage)}, color: color, } }
func NewUpdateProgressPane(progressImage string, loopingImage string) *UpdateProgressPane { return &UpdateProgressPane{ progressImage: util.LoadImage(progressImage), loopingImage: util.LoadImage(loopingImage), } }
func NewLightPane(colorMode bool /*onOffDevices *[]*ninja.ServiceClient, airwheelDevices *[]*ninja.ServiceClient,*/, offImage string, onImage string, conn *ninja.Connection) *LightPane { name := "BrightnessPane" if colorMode { name = "ColorPane" } log := logger.GetLogger(name) log.Infof("Light rate: %s", colorInterval.String()) pane := &LightPane{ onEnable: make(chan bool), colorMode: colorMode, onImage: util.LoadImage(onImage), offImage: util.LoadImage(offImage), log: log, conn: conn, airWheelThrottle: &throttle{delay: colorInterval}, lastTap: time.Now(), gestureSync: &sync.Mutex{}, } if colorMode { pane.onOffState = true } listening := make(map[string]bool) if !colorMode { getChannelServicesContinuous("light", "on-off", /*func(thing *model.Thing) bool { isAccent := strings.Contains(strings.ToLower(thing.Name), "accent") return isAccent == demoAccentMode }*/nil, func(clients []*ninja.ServiceClient, err error) { if err != nil { log.Infof("Failed to update on-off devices: %s", err) } else { log.Infof("Got %d on/off devices", len(clients)) pane.onOffDevices = &clients for _, device := range clients { if _, ok := listening[device.Topic]; !ok { listening[device.Topic] = true device.OnEvent("state", func(state *bool, topicKeys map[string]string) bool { log.Debugf("Got on-off state: %t", *state) // Ignore state updates if its within 500ms of a tap (which will update the display) if time.Since(pane.lastTap) > 500*time.Millisecond { pane.onOffState = *state } return true }) } } } }) } //if demoAccentMode { getChannelServicesContinuous("light", "core/batching", /*func(thing *model.Thing) bool { isAccent := strings.Contains(strings.ToLower(thing.Name), "accent") return isAccent == demoAccentMode }*/nil, func(clients []*ninja.ServiceClient, err error) { if err != nil { log.Infof("Failed to update batching devices: %s", err) } else { log.Infof("Fot %d batching devices", len(clients)) pane.airwheelDevices = &clients } }) //} if colorMode { getChannelServicesContinuous("light", "color", nil, func(clients []*ninja.ServiceClient, err error) { if err != nil { log.Infof("Failed to update color devices: %s", err) } else { for _, device := range clients { if _, ok := listening[device.Topic]; !ok { listening[device.Topic] = true device.OnEvent("state", func(state *channels.ColorState, topicKeys map[string]string) bool { log.Debugf("Got color state: %+v", *state) if state.Mode != "hue" { log.Warningf("Can't handle color mode: %s yet.", state.Mode) return true } // Ignore state updates if its within 2s of an airwheel (which will update the display) if time.Since(pane.lastAirWheelTime) > time.Second { pane.airWheelState = *state.Hue } return true }) } } } }) } else { getChannelServicesContinuous("light", "brightness", nil, func(clients []*ninja.ServiceClient, err error) { if err != nil { log.Infof("Failed to update brightness devices: %s", err) } else { for _, device := range clients { if _, ok := listening[device.Topic]; !ok { listening[device.Topic] = true device.OnEvent("state", func(state *float64, topicKeys map[string]string) bool { log.Infof("Got brightness state: %f", *state) // Ignore state updates if its within 2s of an airwheel (which will update the display) if time.Since(pane.lastAirWheelTime) > time.Second { pane.airWheelState = *state } return true }) } } } }) } return pane }